Wie schreibe ich Metaklasse, die das Erstellen neuer Attribute nach __init __ () verhindern würde?

7

Gegenwärtig überschreibe ich die Klasse ' __setattr__ () gegen Ende der' __init__ () Methode der Klasse, um die Erzeugung neuer Attribute zu verhindern -

%Vor%

Gibt es eine Möglichkeit, das manuelle Überschreiben von __setattr__ () zu vermeiden und dies automatisch mit Hilfe von Metaklassen zu tun?

Am nächsten kam ich -

%Vor%

Gibt es einen allgemeineren Weg dies zu tun, so dass apriori Wissen der Unterklassen-Attribute nicht benötigt wird? Wie kann man die Zeile dctry.update({'x': 0, 'y': 0}) vermeiden und unabhängig von den Namen der Klassenattribute arbeiten?

P.S. - FWIW Ich habe bereits die Optionen __slots__ und namedtuple ausgewertet und festgestellt, dass sie für meine Bedürfnisse nicht ausreichen. Bitte beschränken Sie nicht den Fokus auf das reduzierte Punkte () Beispiel, das ich verwendet habe, um die Frage zu illustrieren; Der eigentliche Anwendungsfall beinhaltet eine viel komplexere Klasse.

    
work.bin 24.09.2015, 12:01
quelle

4 Antworten

6

Wäre das für Ihren Fall sinnvoll?

%Vor%
  

Weitere Informationen zu Wraps finden Sie . em>

Sie müssen wahrscheinlich ein bisschen mehr damit herumspielen, um es eleganter zu machen, aber die allgemeine Idee ist, __setattr__ nur zu überschreiben, nachdem init aufgerufen wurde.

Nachdem dies gesagt wurde, besteht ein gängiger Ansatz darin, object.__setattr__(self, field, value) intern zu verwenden, um den AttributeError zu umgehen:

%Vor%     
ArnauOrriols 02.10.2015, 15:52
quelle
10

Erfinde das Rad nicht neu.

Zwei einfache Möglichkeiten, dies zu erreichen (ohne direkt eine Metaklasse zu verwenden), sind:

  1. namedtuple s
  2. __slots__

Verwenden Sie beispielsweise namedtuple (basierend auf dem Beispiel in der Dokumentation):

%Vor%

Zum Beispiel mit __slots__ :

%Vor%     
shx2 24.09.2015 12:05
quelle
6

Sie brauchen keine Metaklassen, um diese Art von Problem zu lösen.

Wenn Sie die Daten einmal im Voraus erstellen und dann unveränderlich haben wollen, würde ich definitiv eine namedtuple wie shx2 vorschlägt.

Andernfalls definieren Sie einfach eine Sammlung zulässiger Felder für die Klasse und haben __setattr__ check, um zu sehen, ob der Name, den Sie zu setzen versuchen, in der Sammlung zulässiger Felder enthalten ist. Sie müssen die Implementierung von __setattr__ part nicht durch __init__ ändern - es funktioniert während __init__ genau so, wie es später funktioniert. Verwenden Sie eine tuple oder eine frozenset als Datenstruktur für die zulässigen Felder, wenn Sie davon abraten wollen, sie in einer bestimmten Klasse zu mutieren / zu ändern.

%Vor%

Dies kann leicht in eine Basisklasse zur Wiederverwendung ausgegliedert werden:

%Vor%

Ich glaube nicht, dass es als pythonisch gilt, die erlaubten Attribute eines Objekts auf diese Weise zu sperren, und es wird einige Komplikationen für Unterklassen verursachen, die zusätzliche Attribute benötigen (eine Unterklasse wird haben) um sicherzustellen, dass das Feld _allowed_attrs den entsprechenden Inhalt hat).

    
Matt Anderson 29.09.2015 00:20
quelle
1

Ich habe das gleiche Bedürfnis (für eine Entwicklungs-Quick-Hack-API). Ich verwende dafür keine Metaklassen, nur Vererbung:

%Vor%

Dann:

%Vor%

Wenn ich Base ableiten und zusätzliche Attribute hinzufügen muss, verwende ich unlock:

%Vor%

Wenn Base abstrakt ist, können Sie das Sperren überspringen und nur die Childs sperren. Ich habe dann einige Unittests, die überprüfen, dass jede öffentliche Klasse nach init gesperrt ist, um mich zu fangen, wenn ich die Sperre vergesse.

    
agomcas 05.10.2015 09:59
quelle

Tags und Links