用于属性和方法的Python装饰器?
有没有可能创建一个装饰器,让一个方法在用 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
我猜我只需要在 attributeSetter
装饰器中添加一个 __call__,像这样:
def __call__(self, *args, **kwargs):
self.func(*args, **kwargs)
但这样的话,就不能用 "=" 语法来设置值了。
为什么?
我正在共同开发 PsychoPy(一个用于心理物理学中刺激传递的 Python 模块),我们希望在每个刺激参数被设置时进行一些处理,因此需要这个装饰器来避免使用 setter/getter 方法。我们默认记录每个属性的变化,但在某些情况下,用户希望通过额外的 log=False 参数来禁用这个特定设置的日志记录,或者提供更多的参数。因此在这些情况下,如果能像使用函数/方法一样使用属性,那就太好了,因为它本质上确实是这样。
1 个回答
你需要实现一个 __get__
方法,这个方法要返回一个可以调用的对象。你的装饰器已经提供了一个描述符对象,只需要让它在作为属性访问时正常工作就行。
这其实可以很简单,只需将被包装的函数绑定起来(这样你就得到了一个绑定的方法):
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']
中存储的内容);但这条路充满了问题,因为代理对象永远无法真正成为底层属性的值。