使用 getattr 获取包含在描述符中的方法

发布于 2024-10-17 11:41:44 字数 1138 浏览 7 评论 0原文

我有以下描述符,它在调用用 @saveconfig 注释的方法后将配置保存在我的类中:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        def wrapper(*args):
            self.f(instance, *args)
            instance.cfg.write()
            instance.paramcfg.write()
        return wrapper

它的使用方式如下:

class pbtools():
    @saveconfig
    def getip(self, ip):
        (...)

它工作正常。现在我想通过使用 getattr 来获取装饰方法。但由于该方法是由描述符包装的,因此我只能得到 wrapper 结果:

pbt = pbtools()
# results in "<function wrapper at 0x23cced8>:"
method = getattr(pbt, "getip")

How can i access thewrapper method getip with getattr, to be able to call it by它的名字? (当然我无法直接访问该方法,否则我不必这样做)。

附加说明:

该脚本是从命令行调用的。我必须将命令行参数(字符串)映射到具有相同名称的方法才能调用它。此外,我必须将任何其他参数从comamndline映射到该方法的参数,如下所示:

pbtools getip 192.168.178.123 #-> calls getip with parameter 192.168.178.123

由于我无法获取原始方法,所以我不知道它必须映射它们多少个参数。我有几个像这样装饰的方法,因为我想将保存配置的横切关注点移出 pbtools 类中的方法。

I have the following descriptor, which saves the configuration inside my class after a method which is annotated with @saveconfig is called:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        def wrapper(*args):
            self.f(instance, *args)
            instance.cfg.write()
            instance.paramcfg.write()
        return wrapper

It's used like this:

class pbtools():
    @saveconfig
    def getip(self, ip):
        (...)

It works fine. Now i want to get the decorated method by using getattr. But since the method is wrapped by the descriptor, i only get wrapper as a result:

pbt = pbtools()
# results in "<function wrapper at 0x23cced8>:"
method = getattr(pbt, "getip")

How can i access the wrapped method getip with getattr, to be able to call it by it's name? (of course i cannot access the method directly otherwise i would not have to do that).

Additional explanation:

The script is called from commandline. I have to map a command line parameter (string) to a method with the same name to invoke it. Furthermore, i have to map any additional parameters from the comamndline to parameters of the method, like this:

pbtools getip 192.168.178.123 #-> calls getip with parameter 192.168.178.123

Since i cannot get the original method, i don't know how many parameters it has to map them. I have several methods that are decorated like that, because i want to move the cross cutting concern of saving the config out of the methods in the pbtools-class.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情域 2024-10-24 11:41:44

我仍然不能百分百确定我完全理解你的问题。你说“因为我无法获得原始方法,所以我不知道它有多少参数来映射它们”,但是你不需要访问原始方法来通过可变数量的参数调用(因为装饰器有*args)。你可以这样做:

import sys
cmd = sys.argv[1]
params = sys.argv[2:]

pbt = pbtools()

func = getattr(pbt, cmd)
func(*params)

你还可以稍微简化你的装饰器。它实际上并不需要 __get__ 机制。

import functools

def saveconfig(f):
    @functools.wraps(f)
    def wrapper(self, *args):
        f(self, *args)
        self.cfg.write()
        self.paramcfg.write()
    return wrapper

functools.wraps 是一个辅助装饰器,它使包装器模仿原始函数(即它复制函数名称、文档字符串和类似的东西)。它将使调试变得更容易,因为您将知道异常源自何处等。

I'm still not 100% sure I fully understand your problem. You say that "Since i cannot get the original method, i don't know how many parameters it has to map them", but you don't need access to the original method to call by variable number of arguments (since the decorator has *args). You can do something like this:

import sys
cmd = sys.argv[1]
params = sys.argv[2:]

pbt = pbtools()

func = getattr(pbt, cmd)
func(*params)

You can also simplify your decorator a bit. It doesn't really need the __get__ mechanism.

import functools

def saveconfig(f):
    @functools.wraps(f)
    def wrapper(self, *args):
        f(self, *args)
        self.cfg.write()
        self.paramcfg.write()
    return wrapper

The functools.wraps is a helper decorator that makes the wrapper mimic the original function (i.e. it copies the function name, docstring and stuff like that). It will make debugging easier, since you will know where exceptions are originating from etc.

牵你的手,一向走下去 2024-10-24 11:41:44

首先,对评论我的错误感到抱歉,现在你的最后一个代码将无法像你一样工作(你的方法不再被装饰),因为要理解装饰器,你必须看到这个 :

class pbtools():
    @saveconfig
    def getip():
        (...)

相当于:

class pbtools():

    def getip():
        (...)

    getip = saveconfig(getip)

在你最新的情况下 saveconfig 返回 self.f 在我们的代码中等于 getip 所以在这种情况下这个代码:

getip = saveconfig(getip)

相当于:

 getip = getip

所以基本上它不做没有什么。

一种解决方法是将包装的函数保存在包装函数中,如下所示:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        def wrapper(*args):
            self.f(instance, *args)
            instance.cfg.write()
            instance.paramcfg.write()

        wrapper.func = self.f    # Create a new attribute and assign it to the wrapped func.
        return wrapper

现在您可以:

class pbtools():
    @saveconfig
    def getip():
        print "hi"

pbt = pbtools()
method = getattr(pbt, "getip")
print method.func
# <function getip at 0x2363410>
method.func()
# hi

希望这可以有所帮助:)

First of all sorry about the comment my mistake, now for your last code will not work as you which (your method is not decorated anymore) because to understand decorator you have to see this :

class pbtools():
    @saveconfig
    def getip():
        (...)

is equivalent to:

class pbtools():

    def getip():
        (...)

    getip = saveconfig(getip)

and in your latest case saveconfig return self.f which is in our code equal to getip so in this case this code:

getip = saveconfig(getip)

is equivalent to :

 getip = getip

so basically it don't do nothing.

A work around can be by saving the wrapped function in the wrapping one like this:

class saveconfig(object):
    def __init__(self, f):
        self.f = f

    def __get__(self, instance, owner):
        def wrapper(*args):
            self.f(instance, *args)
            instance.cfg.write()
            instance.paramcfg.write()

        wrapper.func = self.f    # Create a new attribute and assign it to the wrapped func.
        return wrapper

and now you can :

class pbtools():
    @saveconfig
    def getip():
        print "hi"

pbt = pbtools()
method = getattr(pbt, "getip")
print method.func
# <function getip at 0x2363410>
method.func()
# hi

Hope this can help :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文