python Wie erstellt man private Klassenvariablen mit setattr oder exec?

8

Ich bin gerade in eine Situation geraten, in der pseudo -private Klassen-Membernamen nicht verunstaltet werden, wenn setattr oder exec verwendet wird.

%Vor%

Ich habe auch exec("self.__%s = %s" % (k, v)) mit dem gleichen Ergebnis probiert:

%Vor%

Doing self.__dict__["_%s__%s" % (self.__class__.__name__, k)] = v würde funktionieren, aber __dict__ ist ein readonly-Attribut.

Gibt es eine andere Möglichkeit, diese psuedo -privaten Klassenmitglieder dynamisch zu erstellen (ohne Hard-Coding im Namen mangling)?

Eine bessere Möglichkeit, meine Frage zu formulieren:

Was macht Python "unter der Haube", wenn ein doppeltes Unterstrich-Attribut ( self.__x ) gesetzt wird? Gibt es eine magische Funktion, die zum Mangeln verwendet wird?

    
chown 16.10.2011, 03:51
quelle

3 Antworten

6

Ich glaube, Python führt während der Kompilierung ein privates Attributmangling aus ... insbesondere tritt es in dem Stadium auf, in dem es die Quelle in eine abstrakte Syntaxstruktur geparst hat und sie in Byte-Code rendert. Dies ist der einzige Zeitpunkt während der Ausführung, an dem die VM den Namen der Klasse kennt, innerhalb deren (lexikalischer) Geltungsbereich die Funktion definiert ist. Dann werden pseudo-private Attribute und Variablen gemangelt und alles andere bleibt unverändert. Dies hat ein paar Implikationen ...

  • Insbesondere String-Konstanten werden nicht gemangelt, weshalb Ihr setattr(self, "__X", x) in Ruhe gelassen wird.

  • Da Mangling vom lexikalischen Umfang der Funktion innerhalb der Quelle abhängt, haben Funktionen, die außerhalb der Klasse definiert sind und dann "eingefügt" werden, keine Fehler, da die Informationen über die Klasse, zu der sie gehören, waren zur Kompilierzeit nicht bekannt.

  • Soweit ich weiß, gibt es keinen einfachen Weg (zur Laufzeit) zu bestimmen, in welcher Klasse eine Funktion definiert wurde ... Zumindest nicht ohne viele inspect Aufrufe, die sich auf die Quelle verlassen Reflexion, um Zeilennummern zwischen den Quellen der Funktion und der Klasse zu vergleichen. Selbst dieser Ansatz ist nicht 100% zuverlässig, es gibt Grenzfälle, die zu fehlerhaften Ergebnissen führen können.

  • Der Prozess ist in Bezug auf das Mangling eigentlich ziemlich unklug - wenn Sie versuchen, auf das __X -Attribut für ein Objekt zuzugreifen, das nicht ist, ist eine Instanz der Klasse lexikalisch definiert Innerhalb, es wird immer noch für diese Klasse Mangle ... Sie können private Klasse Attras in Fällen von anderen Objekten speichern! (Ich würde fast behaupten, dass dieser letzte Punkt ein Feature ist, kein Bug)

Also muss die Variable Mangling manuell gemacht werden, so dass Sie berechnen, was die gemangelte attr sein soll, um setattr aufzurufen.

Was die Mangelung selbst betrifft, so geschieht dies durch die _Py_Mangle -Funktion verwendet die folgende Logik:

  • __X erhält einen Unterstrich und den Klassennamen vorangestellt. Z.B. Wenn es sich um Test handelt, ist das fehlende Attribut _Test__X .
  • Die einzige Ausnahme ist, wenn der Klassenname mit Unterstrichen beginnt, diese sind abgestreift. Z.B. Wenn die Klasse __Test ist, ist der gemangelte attr immer noch _Test__X .
  • Nachgestellte Unterstriche in einem Klassennamen werden nicht entfernt.

Um das Ganze in eine Funktion einzubinden ...

%Vor%

Ich kenne das etwas "hardcodes" den Namen Mangling, aber es ist zumindest zu einer einzigen Funktion isoliert. Es kann dann verwendet werden, um Zeichenketten für setattr :

zu verändern %Vor%

Alternativ verwendet die folgende mangle_attr -Implementierung ein eval, so dass es immer die aktuelle Mangellogik von Python verwendet (obwohl ich denke, dass die oben beschriebene Logik sich nie geändert hat) ...

%Vor%     
Eli Collins 17.10.2011, 04:47
quelle
4

Adressierung dieser:

  

Was macht Python "unter der Haube", wenn es auf ein Double trifft   Unterstrich ( self.__x ) Attribut wird gesetzt? Gibt es eine magische Funktion?   das wird benutzt, um das Mangeln zu machen?

AFAIK, es ist im Wesentlichen speziell im Compiler eingeschlossen. Sobald es in Bytecode ist, ist der Name bereits verstümmelt; Der Dolmetscher sieht den ungeordneten Namen überhaupt nicht und hat keine Ahnung von besonderer Handhabung. Aus diesem Grund funktionieren Verweise über setattr , exec oder durch Nachschlagen in __dict__ nicht. Der -Compiler sieht all diese als Zeichenfolgen und weiß nicht, dass sie irgendetwas mit dem Attributzugriff zu tun haben, sodass er sie unverändert weitergibt. Der Interpreter kennt den Namen mangling nicht, er verwendet sie also direkt.

Die Zeiten, die ich brauchte, um dies zu umgehen, habe ich manuell den gleichen Namen gemacht Mangling, hacky wie das ist. Ich habe festgestellt, dass die Verwendung dieser "privaten" Namen im Allgemeinen eine schlechte Idee ist, es sei denn, Sie wissen, dass Sie sie für den beabsichtigten Zweck benötigen: Erlauben einer Vererbungshierarchie von Klassen, alle den gleichen Attributnamen zu verwenden, aber eine Kopie pro Klasse. Das Bereinigen von Attributnamen mit doppelten Unterstrichen, nur weil es sich um private Implementierungsdetails handelt, scheint mehr Schaden als Nutzen zu verursachen. Ich habe mich darauf beschränkt, einen einzelnen Unterstrich als Hinweis zu verwenden, dass der externe Code ihn nicht berühren sollte.

    
Ben 17.10.2011 05:14
quelle
2

Hier ist der Hack, den ich bisher habe. Verbesserungsvorschläge sind willkommen.

%Vor%     
eryksun 16.10.2011 05:45
quelle