2014-05-08 11 views
5

Angenommen, ich habe eine Klasse, und ich möchte viele ähnliche "Eigenschaften" -Attribute erstellen. Zum Beispiel:Sollte ich eine Deskriptorklasse oder eine Eigenschaftenfabrik verwenden?

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..." 

Offensichtlich gibt es hier eine Menge Wiederholungen, und es wäre nett, dies zu berücksichtigen. Eine Möglichkeit wäre, um eine Funktion zu machen, die eine Eigenschaft gibt:

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..." 

Alternativ könnte ich einen Descriptor-Klasse schreiben:

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" 

Welche am besten/schnellsten/die meisten Pythonic?

+2

Deskriptoren sind genau dafür da. Also, * meiner Meinung nach * sollten Sie Deskriptoren verwenden. Ich habe dies absichtlich kursiv hervorgehoben, um zu verdeutlichen, dass dies hauptsächlich auf Meinungen beruht. Voting zu halten, wie in erster Linie auf der Grundlage von Meinungen – J0HN

+0

Ich würde den Dekorateur auch empfehlen. Wenn Sie das jedoch mit einer Metaklasse kombinieren, können Sie automatisch die Namen der Klassenvariablen aufnehmen, um den Deskriptor zu erstellen, wodurch das Dictionary 'self.data' überflüssig wird. Das wäre etwas besser für Speicher- und Performance-Overhead, aber wahrscheinlich noch wichtiger, Sie müssten sich niemals mit Schlüsselkollisionen oder merkwürdigem Verhalten in dem Fall befassen, in dem der Referent stirbt. –

Antwort

0

Den Deskriptor Idee nehmen und es mit einer Metaklasse mischen, wie ich in den Kommentaren erwähnte.

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" 
Verwandte Themen