【问题标题】:can i decorate a `__getattr__` method in python?我可以在python中装饰一个`__getattr__`方法吗?
【发布时间】:2017-10-15 08:39:23
【问题描述】:

是否可以在python中装饰__getattr__之类的方法?

像这样

@myWrapper
def __getattr__(self,attrName):
    '''
    myCode Goes here
    '''

当我尝试使用类似上面的东西时,我遇到了一些错误:

感谢威廉指出..

我发现了我在代码中犯的错误。

我的意图是做这样的事情

class A:
    def __init__(self):
        self.a=someObject()
    @mywrapper
    def __getattr__(self,argName):
        # call self.a 's  method. If that doesnt have
        # then raise an exception

所以我的方法可能有一些参数,我也需要传递它..

所以我写了这样的东西来测试:

class A:
    def __init__(self):
        self.a=someObject()
    @mywrapper
    def __getattr__(self,argName):
        print('getAttr called for {}'.format(argName))
        def mymethod(*args,**kwargs):
            import ipdb;ipdb.set_trace()
        return mymethod

我的包装代码如下所示:

def mywrapper(method):
    def methodWrapper(obj,name):
        print("In decorator for {}".format(method));
        method(obj,name)
    return methodWrapper

但是当我像 obj.c(1) 一样执行时,我得到以下异常

TypeError: 'NoneType' object is not callable

事实上,装饰器和__getattr__ 中的打印语句都在执行,但我看到了异常..

如何解决?

【问题讨论】:

  • 请粘贴您的装饰器的代码
  • 当然。 myWrapper 到底在做什么?

标签: python python-3.x python-decorators


【解决方案1】:

是的,这是可能的。 __getattr__ 实际上只是另一个函数,尽管 Python 解释器会将其称为它的一种后备机制__init____getattr__固有没有什么特别之处。

然而,按照惯例,带有前后双下划线的名称是 “魔法”对象或存在于用户控制的命名空间中的属性。例如。 __init____import____file__。永远不要发明这样的名字;仅按文档说明使用。”。但这是一个风格指南(您最好遵循)。

但是,由于某种原因,您的 @myWrapper 不尊重(数量)属性

例如,如果您要编写一个包装器:

def myWrapper(func):
    def g(self,attrName):
        return func(self,'foo_%s'%attrName)
    return g

那么就有没有问题了。

如果我们现在定义一个类Foo

class Foo:

    @myWrapper
    def __getattr__(self,attrName):
        print(attrName)

然后我们看到,装饰器工作正常:

>>> foo = Foo()
>>> foo.bla
foo_bla

如您所见,__getattr__ 打印出<b>foo_</b>bla,这意味着myWrapper 在属性前添加了foo_

至于您的(已编辑)问题,我猜它之所以说“TypeError: 'NoneType' object is not callable”是因为装饰器应该返回一个(新)函数

可能你的装饰器看起来像:

def mywrapper(func):
    def g(self,attrName):
        return func(self,attrName)

所以你可能忘了打电话给return g

编辑:根据您更新的问题,问题是您返回内部__getattr__ 调用的结果:

def mywrapper(method):
    def methodWrapper(obj,name):
        print("In decorator for {}".format(method));
        return method(obj,name) -- with a return statement
    return methodWrapper

这很重要,因为您 __getattr__ 返回一个函数。请注意,在mywrapper 的上下文中,method原始 __getattr__methodWrapper 是新的__getattr__。所以你应该传递原始__getattr__ 的结果。现在你不用它做任何事情并返回(默认)None

【讨论】:

  • 我的包装器这样做:我的包装器代码看起来像 def mywrapper(method): def methodWrapper(obj,name): print("In decorator");method(obj,name) return methodWrapper跨度>
  • @ArunKalirajaBaskaran:在您的内部 method(obj,name) 电话中没有 no 回报。这意味着您的__getattr__ 返回的内容将永远返回。
  • 我很抱歉..我仍然无法得到它.. myWrapper 是我的装饰器,它返回 methodWrapper func 对象。并且我的 getattr 用 myWrapper 装饰。我在 getattr 内定义了一个函数 mymethod,其中我也有一个断点,我从 返回这个 mymethod 函数指针b>getattr.. 我错过了什么! :(
  • @ArunKalirajaBaskaran:这有点令人困惑。您能否编辑您的问题,编写一个(最小完整)版本的包装器以及一条错误消息。不是我想烦人,只是目前有点模糊。
  • @WillemVonOsem 感谢您指出错误.. 再澄清一下.. 所以在这种情况下,我不能编写一个通用包装器,而不能用于我在类中定义的两种方法[case-1] 和我尚未定义的方法(将由 getattr 处理)[case-2].. 因为在 [case-1] 中,methodWrapper 中的第二个参数必须是类方法的参数,在 [case-2] 中,它将是用户调用的属性的名称。有什么方法可以为这两种情况编写通用包装器吗?
猜你喜欢
  • 1970-01-01
  • 2011-07-21
  • 2012-12-15
  • 1970-01-01
  • 2011-08-10
  • 2021-08-25
  • 2012-07-29
  • 1970-01-01
相关资源
最近更新 更多