属性和方法的 Python 装饰器?

Posted

技术标签:

【中文标题】属性和方法的 Python 装饰器?【英文标题】:Python decorator for attribute and method? 【发布时间】:2014-06-25 14:26:42 【问题描述】:

如果使用class.something = 2 为方法分配值,是否可以有一个装饰器使方法像属性一样工作,如果像class.something(2, True) 那样调用它,是否可以像方法一样工作?

我目前拥有的

更具体地说,我目前有以下内容

class attributeSetter(object):
   ''' Makes functions appear as attributes. Takes care of autologging.'''
   def __init__(self, func):
       self.func = func

   def __set__(self, obj, value):
       return self.func(obj, value)

   def __repr__(self):
       return repr(self.__getattribute__)

因此,使用带有修饰方法的此类:

class myClass(object):
   @attributeSetter  # decorated!
   def myAttrib(self, value, add=False, subtract=False):
       if add:
           value += 2
       if subtract:
           value -= 2
       self.__dict__['myAttrib'] = value

我可以这样做:

instance = myClass()
instance.myAttrib = 2  # sets the value to 2
instance.myAttrib *= 4  # now 8
print instance.myAttrib  # 8

我想要什么

但我还希望能够做到这一点:

instance.myAttrib(3, add=True)  # now 8 + 3 + 2 = 13
instance.myAttrib(0, subtact=True)  # now 13 + 0 - 2 = 11
print instance.myAttrib  # 11

我的直觉是我只需要像这样将 __call__ 添加到 attributeSetter 装饰器:

def __call__(self, *args, **kwargs):
    self.func(*args, **kwargs)

但它不会让我使用“=”语法设置值。

为什么?

我正在共同开发 PsychoPy(心理物理学中用于刺激传递的 python 模块),我们希望在设置每个刺激参数时进行一些处理,因此装饰器避免使用 setter/getter 方法。我们默认记录每个属性更改,但在某些情况下,用户希望使用额外的 log=False 参数禁用此特定设置的记录,或提供更多参数。因此,在这些情况下,如果可以像函数/方法一样使用属性,那就太好了。

【问题讨论】:

我在后期注意到一点,您希望 instance.myAttrib 既是托管值,又是可调用的 instance.myAttrib()。恐怕你不能两者兼得。 instance.method_name 返回一个绑定方法,您可以将其存储在变量中并稍后调用。 instance.method_name() 中的() 属性被查找之后应用。 换句话说,无论是类还是描述符对象上的__get__ 方法都不会知道您将如何处理返回的属性。这完全取决于__call__ 感谢@MartijnPieters!我已经编辑了“类方法”。这是一个悲伤的消息,但你已经很好地解释了原因。稍后我会接受答案,这取决于在此期间是否给出了更多答案。 【参考方案1】:

您需要实现一个 __get__ method 返回一个可调用对象;您的装饰器已经提供了一个描述符对象,只需在作为属性访问时使其工作。

这可以像转身和绑定被包装的函数一样简单(这样你就得到了一个绑定的方法):

class attributeSetter(object):
    ''' Makes functions appear as attributes. Takes care of autologging.'''
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        return self.func.__get__(instance, owner)

    def __set__(self, obj, value):
        return self.func(obj, value)

然而,这使得它与简单地访问属性不兼容! instance.myAttrib 现在返回一个绑定方法!你不能在这里同时拥有它。方法是简单的绑定属性(通过描述符协议传递的属性),碰巧是可调用的。

你当然可以从__get__返回一个代理对象;一个实现__call__ 方法并尝试尽可能多地作为底层托管实例属性(您的函数存储在self.__dict__['myAttrib'] 中的内容)的方法;但这条路径充满了问题,因为代理对象永远无法真正成为底层属性值。

【讨论】:

以上是关于属性和方法的 Python 装饰器?的主要内容,如果未能解决你的问题,请参考以下文章

Python中的property类和@property装饰器

Python入门-6面向对象编程:06私有属性和私有方法(实现封装)-@property装饰器-get和set方法-总结

Python - 函数属性方法装饰器

python中使类方法像属性一样被使用

Python面向对象 | 类属性

如何在 Python 的方法装饰器中使用类属性?