如何使用接受名称列表作为参数的装饰器向类添加属性?
我想通过一个装饰器给一个类添加很多虚拟属性,像这样:
def addAttrs(attr_names):
def deco(cls):
for attr_name in attr_names:
def getAttr(self):
return getattr(self, "_" + attr_name)
def setAttr(self, value):
setattr(self, "_" + attr_name, value)
prop = property(getAttr, setAttr)
setattr(cls, attr_name, prop)
setattr(cls, "_" + attr_name, None) # Default value for that attribute
return cls
return deco
@addAttrs(['x', 'y'])
class MyClass(object):
pass
不幸的是,这个装饰器似乎保留了attr_name
的引用,而不是它的内容。因此,MyClass.x
和MyClass.y
都访问MyClass._y
:
a = MyClass()
a.x = 5
print a._x, a._y
>>> None, 5
a.y = 8
print a._x, a._y
>>> None, 8
我需要做什么才能得到预期的效果呢?
2 个回答
3
Python不支持块级作用域,只支持函数级作用域。这意味着在循环中定义的任何变量,循环外部也能访问,并且它的值是循环结束时的最后一个值。如果你想得到你想要的结果,就需要在循环中使用闭包:
def addAttrs(attr_names):
def deco(cls):
for attr_name in attr_names:
def closure(attr):
def getAttr(self):
return getattr(self, "_" + attr)
def setAttr(self, value):
setattr(self, "_" + attr, value)
prop = property(getAttr, setAttr)
setattr(cls, attr, prop)
setattr(cls, "_" + attr, None)
closure(attr_name)
return cls
return deco
通过使用闭包closure
,在getAttr
和setAttr
中赋值的属性会被正确地作用域控制。
编辑:修正了缩进问题
14
你差不多就要成功了。只差一点小问题。当你创建内部函数的时候,要把当前的 attr_name
的值绑定到获取器和设置器函数里:
def addAttrs(attr_names):
def deco(cls):
for attr_name in attr_names:
def getAttr(self, attr_name=attr_name):
return getattr(self, "_" + attr_name)
def setAttr(self, value, attr_name=attr_name):
setattr(self, "_" + attr_name, value)
prop = property(getAttr, setAttr)
setattr(cls, attr_name, prop)
setattr(cls, "_" + attr_name, None) # Default value for that attribute
return cls
return deco
@addAttrs(['x', 'y'])
class MyClass(object):
pass
这样就能得到你想要的结果:
>>> a = MyClass()
>>> a.x = 5
>>> print a._x, a._y
5 None
>>> a.y = 8
>>> print a._x, a._y
5 8
希望这能帮到你。祝你装饰愉快 :-)