2012-02-03 13 views
5

Chciałbym dodać wiele dummy-właściwości do klasy za pomocą dekoratora, tak:Jak mogę dodać właściwości do klasy za pomocą dekoratora, który pobiera listę nazw jako argument?

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 

Niestety decoarator wydaje się zachować odniesienie attr_name zamiast jego zawartości. Dlatego MyClass.x i MyClass.y dostęp zarówno MyClass._y:

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

Co muszę zmienić, aby uzyskać oczekiwane zachowanie?

Odpowiedz

7

Prawie wszystko działało. Jest tylko jedna nitka. Podczas tworzenia funkcje wewnętrzne, wiążą bieżącą wartość attr_name do funkcji getter i setter:

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 

To daje oczekiwany wynik:

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

nadzieję, że to pomaga. Szczęśliwe dekorowanie :-)

2

Python nie obsługuje zakresu bloków, tylko poziom funkcji. Dlatego każda zmienna przypisana w pętli będzie dostępna poza pętlą jako ostatnia dostępna wartość. Aby uzyskać wynik, którego szukasz, trzeba będzie użyć zamknięcie w pętli:

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 

Korzystanie zamknięcie closure atrybuty przypisane ciągu getAttr i setAttr zostanie zawężona poprawnie.

EDYCJA: poprawione wcięcie