2012-04-03 8 views
3

Ich versuche, einen Weg zu finden, dict Werte in ein class eingekapselt zu setzen, zum Beispiel __getattr__ ich mit dem internen dict Wert zurückgeben kann jedoch die __setattr__ auch genannt wird, wenn Attribute vorhanden ist, meine Implementierung hässlich machen. Das folgende Beispiel ist vereinfacht meine eigentliche Klasse erbt von einem Subjectclass (dem Thema des Beobachter Muster)Gegenstück von __getattr__

ich so etwas wie dies zu erreichen versuchen:

obj = Example() 
obj.username = 'spidername' # all OK username is a key in the internal dict 
# but company is not a key in the internal dict so 
obj.company = 'ABC' # will raise AttributeError 

, und ich fragen, ob es eine ist besserer Weg, als die Art, wie ich unten tue:

class Example(object): 
    def __init__(self, table=None): 
     self._fields = {} 
     self._table = table 

    def _set_fields(self): 
     """ 
     this method will be implemented by 
     subclasses and used to set fields names and values 
     i.e. 
     self._field['username'] = Field(default='unknown', is_primary=False) 
     """ 
     raise NotImplementedError 

    def __getattr__(self, name): 
     """ 
     great this method is only called when "name" 
     is not an attribute of this class 
     """ 
     if name in self._fields: 
      return self._fields[name].value 
     return None 

    def __setattr__(self, name, value): 
     """ 
     not so great, this method is called even for 
     attributes that exists in this class 

     is there a better way to do the following?    
     this can be in __init__, but its still ugly 
     """ 
     attribs = ['_fields', '_table'] 
     if name in attribs: 
      super(Example, self).__setattr__(name, value) 
     else: 
      if name in self._fields: 
       self._fields[name].value = value 
      else: 
       raise AttributeError 

EDIT: angepasst Kommentar in Code, missin Anführungszeichen hinzugefügt

+0

Eine Nebenbemerkung: Es gibt bereits viele gute Python-Bibliotheken für die Object Relational Mapping (ORM), zum Beispiel SQLAlchemy; wenn das das ist, was du versuchst zu tun. –

+0

danke, ja ich weiß, es gibt auch Storm und SQLObject, die ich kenne. aber ich mag etwas einfacher und das ist mein eigenes. – andrea

Antwort

3

Das Problem ist, dass die Attribute nicht existieren, wenn sie zuerst zugeordnet sind. In __init__, wenn Sie zuerst ein Dict zu _fields zuweisen, ist _fields kein Attribut. Es wird nur ein vorhandenes Attribut, nachdem es zugewiesen wurde. Sie können __slots__ verwenden, wenn Sie im Voraus wissen, was die Attribute sind, aber meine Vermutung ist, dass Sie nicht. Also mein Vorschlag wäre, diese in die Instanz dict manuell einzufügen sein:

class Example(object): 
    def __init__(self, table=None): 
     self.__dict__['_fields'] = {} 
     self.__dict__['_table'] = table 

    ... 

    def __setattr__(self, name, value): 
     if name in self._fields: 
      self._fields[name].value = value 
     else: 
      raise AttributeError 

jedoch mit dieser Implementierung der einzige Weg, die Sie hinzufügen können oder Instanzattribute später durch __dict__ wäre ändern. Aber ich nehme an, das ist nicht wahrscheinlich.

+1

Ich glaube nicht, dass er den "hasattr" überhaupt brauchen würde - ich hatte nicht den Eindruck, dass er sie wieder außerhalb von "__init__" basierend auf ihm sagen musste: "Ich weiß, dass das in" __init__' sein könnte. in seinem Kommentar in "__setattr__". – agf

+0

Richtig, ich werde die Antwort für diesen Fall aktualisieren. – aquavitae

+0

Ich habe zuerst '__slots__' versucht, aber nicht funktioniert, da'__setattr__' immer noch aufgerufen wurde, und das erzeugte ein Problem, weil ich in'__setattr__' nicht weiß, ob' name' auf ein legitimes Klassenattribut '' verweist __init__' oder ein Schlüssel in 'self._fields', Ihre Lösung hat perfekt funktioniert. Die einzige Sache, wie auch gesagt, ist, dass "hasattr" in "__setattr__" nicht mehr benötigt wird. danke – andrea

2

FWIW, kann Ihr Gesamtziel direkt nur __slots__ erreicht werden durch Einsatz:

>>> class Example(object): 
     __slots__ = ['username'] 

>>> obj = Example() 
>>> obj.username = 'spiderman' 
>>> obj.company = 'ABC' 

Traceback (most recent call last): 
    File "<pyshell#18>", line 1, in <module> 
    obj.company = 'ABC' 
AttributeError: 'Example' object has no attribute 'company' 
+0

selbst mit __slots__, __setattr__ wird immer noch für jedes Attribut aufgerufen, und ich will das nicht, ich möchte __setattr__ nur aufrufen, wenn der Client-Code einen Wert für das interne dict setzen will. – andrea

Verwandte Themen