2013-04-29 5 views
6

Nehmen Sie die folgende KlasseWie Eigenschaften Auf eine Python-Klasse

class Person(object): 

def __init__(self, first_name, last_name): 
    self.first_name = first_name 
    self.last_name = last_name 

Wie kann ich verhindern, dass die folgende Nutzungsbeschränkung?

p1 = Person('Foo', 'Bar') 
p1.firstname='Fooooooo' 

Der obige Code erfolgreich in Python ausführen wird, wurde jedoch ein Fehler mit dem Namen der Eigenschaft gemacht, dh seine _ zwischen first und name fehlt

UPDATE: Das klingt wie „Monkey Patchen ", warum möchte ich das tun?

Meine Absicht ist einfach zu helfen, den Benutzer davon abzuhalten, die falsche Eigenschaft zu setzen, den Code ausführen zu lassen und unerwartetes Verhalten zu sehen und den Fehler nicht sofort zu bemerken.

Was kann der Pythonic Weg hier empfehlen?

+0

Ich denke, das ist ein Beispiel dafür, was man "Affenflicken" nennt, und ich denke, es ist eines der Dinge, die die Leute an Python lieben. Es klingt also ein bisschen wie du mit der Sprache kämpfst. – BenDundee

+1

Wenn Sie wirklich müssen, kann '__slots__' erreichen, was Sie tun möchten. Jedoch sollte es wirklich nicht dafür verwendet werden ... Ich würde das "__slots__" missbrauchen. Letztendlich ist dies der Grund, warum Sie eine konsistente API und eine gute Dokumentation bereitstellen müssen. Wenn ein Benutzer einen Fehler erhält, stellt er fest, dass er Ihre API nicht korrekt verwendet hat. – mgilson

+2

@BenDundee: Beim Monkeypatching geht es hauptsächlich um das Hinzufügen/Ändern von Verhalten zur Laufzeit. Wenn Sie sich auf die gleichen Mechanismen verlassen, ist das obige Beispiel definitiv ein Tippfehler, kein Monkeypatch;) –

Antwort

7

Zunächst ist es fast immer eine schlechte Idee, so etwas zu tun. Wenn nur der Grund, warum Sie wollen, dass Sie sicherstellen, dass Sie keine Tippfehler machen - es gibt bessere Werkzeuge dafür (glaube IDE oder pylint). Wenn Sie 100% positiv sind, dass Sie so etwas brauchen, sind hier zwei Möglichkeiten:

Erste Möglichkeit - Sie können dies mit der Verwendung __setattr__ Methode tun. Siehe python __setattr__ documentation

class Person(object): 

    def __init__(self, first_name, last_name): 
     self.__dict__['first_name'] = first_name 
     self.__dict__['last_name'] = last_name 
    def __setattr__(self, name, value): 
     if name in self.__dict__: 
      super(Person, self).__setattr__(name, value) 
     else: 
      raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name)) 

und Ausgang:

In [49]: a = Person(1, 2) 

In [50]: a.a = 2 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>() 
----> 1 a.a = 2 

/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in __setattr__(self, name, value) 
     8    super(Person, self).__setattr__(name, value) 
     9   else: 
---> 10    raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name)) 

AttributeError: Person has no attribute a 

Alternativ können Sie tun dies mit __slots__ (python __slots__ documentation):

class Person(object): 
    __slots__ = ("first_name", "last_name") 

    def __init__(self, first_name, last_name): 
     self.first_name = first_name 
     self.last_name = last_name 

Ausgang:

In [32]: a = Person("a", "b") 

In [33]: a.first_name 
Out[33]: 'a' 

In [34]: a.a = 1 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>() 
----> 1 a.a = 1 

AttributeError: 'Person' object has no attribute 'a' 

Der erste Weg ist flexibler, da er es ermöglicht, diesen Code durch die direkte Verwendung von __dict__ noch weiter zu hacken, aber das wäre noch mehr falsch als jetzt. Der zweite Ansatz reserviert den Platz für eine bestimmte Anzahl von Instanzvariablen (Referenzen), was weniger Speicherverbrauch bedeutet.

+0

Während (fast - das sollte 'super .__ setattr__' heißen, nicht 'super .__ setitem__') technisch korrekt sein (" die schlimmste Art von Korrektem "), ist dies ein perfektes Beispiel dafür, was man in Python NICHT tun sollte. Der Kampf gegen die Sprache ist nur eine Verschwendung von Zeit und eine wichtige PITA. –

+0

Tut mir leid wegen der Menge, korrigiert das. Ich stimme zu, dass es meistens falsch ist, aber ein Fall, in dem ich es benutzt habe - und es nützlich fand - ist das Erstellen eines ORM (du kämpfst dort sowieso mit der Sprache). Und auf lange Sicht war es keine Zeitverschwendung. –

+0

+1 für '__slots__' – Aprillion

Verwandte Themen