2012-02-03 10 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

あなたはほとんどそれを働いていました。ただ1つのニットがあります。内部関数を作成するときは、getterとsetter関数にattr_nameの現在の値をバインドします

>>> a = MyClass() 
>>> a.x = 5 
>>> print a._x, a._y 
5 None 
>>> a.y = 8 
>>> print a._x, a._y 
5 8 

・ホープ、このことができます:

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 

これは期待どおりの結果を生成します。ハッピーデコレーション:-)

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 

閉鎖closuregetAttrsetAttrが正しくスコープされます以内に割り当てられた属性を使用しました。

EDIT:修正インデント

関連する問題