Ich bin gerade in eine Situation geraten, in der pseudo -private Klassen-Membernamen nicht verunstaltet werden, wenn setattr
oder exec
verwendet wird.
Ich habe auch exec("self.__%s = %s" % (k, v))
mit dem gleichen Ergebnis probiert:
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?
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
. __Test
ist, ist der gemangelte attr immer noch _Test__X
. 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
:
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) ...
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.
Tags und Links python attributes private-members name-mangling