2014-05-08 11 views
5

Powiedz, że mam zajęcia i chcę zrobić wiele podobnych atrybutów "właściwości" stylu. Na przykład:Czy powinienem używać klasy deskryptorów lub fabryki nieruchomości?

class Foo(object): 
    @property 
    def foo(self): 
     return self._foo 

    @foo.setter 
    def foo(self, val): 
     self.update() 
     self._foo = val 

    @property 
    def bar(self): 
     return self._bar 

    @bar.setter 
    def bar(self, val): 
     self.update() 
     self._bar = val 

    @property 
    def baz(self): 
     return self._baz 

    @baz.setter 
    def baz(self, val): 
     self.update() 
     self._baz = val 

    def update(self): 
     print "Updating..." 

Tutaj jest dużo powtórzeń i byłoby miło to podkreślić. Jednym ze sposobów byłoby utworzyć funkcję, która zwraca właściwość:

def create_property(var): 
    def _setter(self, val): 
     self.update() 
     setattr(self, '_' + var, val) 

    def _getter(self): 
     return getattr(self, '_' + var) 

    return property(_getter, _setter) 

class Foo(object): 
    foo = create_property("foo") 
    bar = create_property("bar") 
    baz = create_property("baz") 

    def update(self): 
     print "Updating..." 

Ewentualnie mógłbym napisać klasę Eurovoc:

class UpdateOnChange(object): 
    def __init__(self): 
     self.data = WeakKeyDictionary() 

    def __get__(self, instance, owner): 
     try: 
      return self.data[instance] 
     except KeyError: 
      raise AttributeError("attribute not set") 

    def __set__(self, instance, value): 
     instance.update() 
     self.data[instance] = value 

class Foo(object): 
    foo = UpdateOnChange() 
    bar = UpdateOnChange() 
    baz = UpdateOnChange() 

    def update(self): 
     print "Updating" 

która jest najlepsza/najszybszy/najbardziej pythonowy?

+2

Deskryptory są właśnie po to. Tak więc * według mojej opinii * powinieneś używać deskryptorów. Podzieliłem to celowo, aby podkreślić, że jest to w większości oparte na opiniach. Głosowanie, aby zawiesić, jak głównie oparte na opiniach – J0HN

+0

Polecam również dekoratora. Jednak jeśli połączysz to z metaklasem, możesz automatycznie przetworzyć nazwy zmiennych klasowych, aby skonstruować deskryptor, tym samym eliminując potrzebę używania słownika 'self.data'. Byłoby to nieco lepsze dla pamięci i wydajności, ale prawdopodobnie, co ważniejsze, nigdy nie musiałbyś radzić sobie z kluczowymi kolizjami lub dziwnym zachowaniem w przypadku śmierci referencji. –

Odpowiedz

0

Podejmowanie pomysłu deskryptora i mieszanie go z metaklasem, o czym wspomniałem w komentarzach.

class UpdateOnChange(object): 
    def __init__(self, name): 
     self.name = name 
     self._name = '_' + name 

    def __get__(self, instance, owner): 
     try: 
      return getattr(instance, self._name) 
     except AttributeError: 
      raise AttributeError('%s has no attribute %s.' % (instance, self.name)) 

    def __set__(self, instance, value): 
     # Assume first assignment is initialization and does not require update. 
     if not hasattr(instance, self._name): 
      setattr(instance, self._name, value) 
     else: 
      instance.update() 
      setattr(instance, self._name, value) 

class UpdateOnChangeMeta(type): 

    def __new__(cls, name, bases, attrs): 
     for attr_name in attrs: 
      if attrs[attr_name] == UpdateOnChange: 
       attrs[attr_name] = UpdateOnChange(attr_name) 
     return type.__new__(cls, name, bases, attrs) 

class Foo(object): 

    __metaclass__ = UpdateOnChangeMeta 
    foo = UpdateOnChange 
    bar = UpdateOnChange 
    baz = UpdateOnChange 

    def update(self): 
     print "Updating"