2012-02-03 61 views
5

我想通過一個裝飾了許多僞屬性添加到一個類,像這樣:如何使用將名稱列表作爲參數的裝飾器將屬性添加到類中?

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 

不幸的是,decoarator似乎保持attr_name,而不是它的內容的參考。因此,MyClass.xMyClass.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 

我有什麼改變來獲得預期的行爲?

回答

7

你幾乎有它的工作。只有一個尼特。當創建內部函數,的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 

希望這有助於。快樂的裝飾:-)

2

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,內getAttrsetAttr將被正確作用域分配的屬性。

編輯:更正後的縮進

相關問題