1859 Stimmen

Welche Bedeutung haben einfache und doppelte Unterstriche vor einem Objektnamen?

Kann jemand bitte erklären, die genaue Bedeutung der mit einfachen und doppelten führenden Unterstrichen vor einem Objektnamen in Python, und der Unterschied zwischen beiden?

Bleibt diese Bedeutung auch gleich, unabhängig davon, ob es sich bei dem betreffenden Objekt um eine Variable, eine Funktion, eine Methode usw. handelt?

6 Stimmen

Eine tolle kurze Antwort aus einem anderen Thread: stackoverflow.com/a/8689983/911945

1 Stimmen

Siehe auch dies ausgezeichnet Hier finden Sie eine Antwort auf die Frage nach einfachen und doppelten Unterstrichen und eine Demonstration der "Namensverwechslung"! Antwort auf "Private Mitglieder in Python"

1624voto

Andrew Keeton Punkte 20477

Einzelne Unterstreichung

Namen in einer Klasse mit einem führenden Unterstrich dienen lediglich dazu, anderen Programmierern anzuzeigen, dass das Attribut oder die Methode privat sein soll. Mit dem Namen selbst wird jedoch nichts Besonderes gemacht.

Um zu zitieren PEP-8 :

_single_leading_underscore: schwacher Indikator für den "internen Gebrauch". z.B. from M import * importiert keine Objekte, deren Name mit einem Unterstrich beginnt.

Doppelte Unterstreichung (Namensverwechslung)

Von die Python-Dokumente :

Jeder Bezeichner der Form __spam (mindestens zwei führende Unterstriche, höchstens ein nachgestellter Unterstrich) wird textlich ersetzt durch _classname__spam , donde classname ist der aktuelle Klassenname ohne führende(n) Unterstrich(e). Dieses Mangling erfolgt ohne Rücksicht auf die syntaktische Position des Bezeichners, so dass es verwendet werden kann, um klassenprivate Instanz- und Klassenvariablen, Methoden, in Globals gespeicherte Variablen und sogar in Instanzen gespeicherte Variablen zu definieren. private dieser Klasse auf Instanzen anderer Klassen.

Und eine Warnung von derselben Seite:

Namensmangling soll Klassen eine einfache Möglichkeit geben, "private" Instanzvariablen und Methoden zu definieren, ohne sich um Instanzvariablen kümmern zu müssen, die von abgeleiteten Klassen definiert wurden, oder um den Umgang mit Instanzvariablen durch Code außerhalb der Klasse. Beachten Sie, dass die Mangling-Regeln in erster Linie dazu dienen, Unfälle zu vermeiden; es ist immer noch möglich, dass eine bestimmte Person auf eine Variable zugreift oder sie verändert, die als privat angesehen wird.

Beispiel

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}

30 Stimmen

Was ist, wenn ein Variablenname mit 2 Unterstrichen deklariert wird, der nicht in der Klasse enthalten ist? Dann ist es eine ganz normale Variable, oder?

5 Stimmen

Was bedeutet "einfach"? __ doppelter Unterstrich als Variablenname? wie a, __ = foo()

174 Stimmen

Diese Antwort ist äußerst irreführend, da sie den Leser glauben lässt, dass dunderscore verwendet wird, um Instanzattribute "superprivat" zu machen. Dies ist no der Fall, da hier erklärt von Raymond Hettinger, der ausdrücklich darauf hinweist, dass dunderscore fälschlicherweise verwendet wird, um Mitglieder als privat zu kennzeichnen, während es eigentlich für das Gegenteil von privat gedacht war.

555voto

Ned Batchelder Punkte 342778

__foo__ : Dies ist nur eine Konvention, eine Möglichkeit für das Python-System, Namen zu verwenden, die nicht mit Benutzernamen in Konflikt geraten.

_foo : Dies ist nur eine Konvention, eine Möglichkeit für den Programmierer, anzuzeigen, dass die Variable privat ist (was immer das in Python bedeutet).

__foo : dies hat eine echte Bedeutung: der Interpreter ersetzt diesen Namen durch _classname__foo um sicherzustellen, dass sich der Name nicht mit einem ähnlichen Namen in einer anderen Klasse überschneidet.

Keine andere Form von Unterstrichen hat in der Python-Welt eine Bedeutung.

Bei diesen Konventionen gibt es keinen Unterschied zwischen Klasse, Variable, Global usw.

10 Stimmen

Ich bin gerade auf __foo und neugierig. Wie kann es zu Überschneidungen mit ähnlichen Methodennamen bei anderen Klassen kommen? Ich meine, man muss immer noch auf sie zugreifen wie instance.__foo() (wenn es nicht vom Interpreter umbenannt wurde), richtig?

183 Stimmen

Dieser Typ stellt fest, dass from module import * importiert keine Objekte, die mit einem Unterstrich versehen sind. Deshalb, _foo ist mehr als nur ein Kongress.

7 Stimmen

@Bibhas: wenn Klasse B Unterklassen Klasse A , und beide implementieren foo() entonces B.foo() übersteuert die .foo() geerbt von A . Eine Instanz von B können nur zugreifen auf B.foo() , außer über super(B).foo() .

384voto

Alex Martelli Punkte 805329

Ausgezeichnete Antworten bis jetzt, aber es fehlen noch einige Kleinigkeiten. Ein einzelner führender Unterstrich ist nicht gerade nur eine Konvention: Wenn Sie from foobar import * und Modul foobar definiert nicht eine __all__ Liste, die aus dem Modul importierten Namen nicht auch solche mit einem führenden Unterstrich. Sagen wir, es ist meist eine Konvention, da dieser Fall eine ziemlich obskure Ecke ist;-).

Die Konvention der Führung und des Unterstrichs ist weit verbreitet, nicht nur für privat Namen, sondern auch für das, was man in C++ als geschützt z. B. Namen von Methoden, die vollständig von Unterklassen überschrieben werden sollen (auch solche, die haben überschrieben werden, da sie in der Basisklasse raise NotImplementedError !-) sind oft einleitende Unterstrichnamen, um dem Code anzuzeigen mit Instanzen dieser Klasse (oder Unterklassen), dass diese Methoden nicht direkt aufgerufen werden sollen.

Um zum Beispiel eine Thread-sichere Warteschlange mit einer anderen Warteschlangen-Disziplin als FIFO zu erstellen, importiert man Queue, unterteilt Queue.Queue und überschreibt Methoden wie _get y _put Der "Client-Code" ruft niemals diese ("Hook"-)Methoden auf, sondern die öffentlichen ("organisierenden") Methoden wie put y get (dies ist bekannt als die Vorlage Methode Entwurfsmuster - siehe z. B. ici für eine interessante Präsentation, die auf einem Video eines Vortrags von mir zu diesem Thema basiert, mit zusätzlichen Zusammenfassungen des Transkripts).

Edit: Die Video-Links in der Beschreibung der Vorträge sind jetzt defekt. Sie können die ersten beiden Videos finden ici y ici .

2 Stimmen

Wie entscheiden Sie also, ob Sie _var_name oder verwenden var_name + ausgenommen von __all__ ?

3 Stimmen

@endolith Verwenden Sie einen führenden Unterstrich, um dem Leser Ihres Codes zu signalisieren, dass er dies wahrscheinlich nicht verwenden sollte (z. B. weil Sie es in Version 2.0 oder sogar 1.1 ändern könnten); verwenden Sie explizit __all__ wenn Sie das Modul from spam import * freundlich (auch beim interaktiven Dolmetscher). In den meisten Fällen lautet die Antwort also beide .

0 Stimmen

@AlexMartelli Wird diese importbezogene Regel irgendwo in den Dokumenten oder anderswo rechtlich diskutiert?

259voto

NickCSE Punkte 185

._variable ist halbprivat und nur für Konferenzen gedacht

.__variable wird oft fälschlicherweise als superprivat angesehen, während seine eigentliche Bedeutung nur darin besteht, dass er sich mit ungewollten Zugriff verhindern [1]

.__variable__ ist normalerweise für eingebaute Methoden oder Variablen reserviert

Sie können weiterhin auf .__mangled Variablen, wenn Sie das unbedingt wollen. Durch die doppelten Unterstriche wird die Variable lediglich umbenannt, z. B. in instance._className__mangled

Exemple :

class Test(object):
    def __init__(self):
        self.__a = 'a'
        self._b = 'b'

>>> t = Test()
>>> t._b
'b'

t._b ist zugänglich, weil es nur durch Konvention verborgen ist

>>> t.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'

t.__a wird nicht gefunden, weil es aufgrund von Namensverwechslung nicht mehr existiert

>>> t._Test__a
'a'

Durch den Zugriff auf instance._className__variable anstelle des doppelten Unterstrichs Name können Sie auf den verborgenen Wert zugreifen

0 Stimmen

Aber was ist, wenn "__a" eine Klassenvariable war, dann können Sie nicht darauf zugreifen, auch mit den Anweisungen aus Python docs..

0 Stimmen

Können Sie bitte Ihre Antwort mit einem Beispiel für einen doppelten Unterstrich in Bezug auf die Vererbung aktualisieren?

2 Stimmen

._variable ist nach den obigen Beiträgen und PEP-8 nicht nur eine Konvention: " from M import * importiert keine Objekte, deren Namen mit einem Unterstrich beginnen.". Im vorliegenden Fall, in dem es als Klassenattribut angezeigt wird, ändert sich jedoch nichts.

171voto

PythonDev Punkte 4219

Einfacher Unterstrich am Anfang:

Python hat keine echten privaten Methoden. Stattdessen bedeutet ein Unterstrich am Anfang eines Methoden- oder Attributnamens, dass Sie nicht auf diese Methode zugreifen sollten, da sie nicht Teil der API ist.

class BaseForm(StrAndUnicode):

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

    errors = property(_get_errors)

(Dieser Codeschnipsel stammt aus dem Quellcode von django: django/forms/forms.py). In diesem Code, errors ist eine öffentliche Eigenschaft, aber die Methode, die diese Eigenschaft aufruft, _get_errors, ist "privat", so dass Sie nicht darauf zugreifen sollten.

Zwei Unterstriche am Anfang:

Dies führt zu einer Menge Verwirrung. Es sollte nicht verwendet werden, um eine private Methode zu erstellen. Sie sollte verwendet werden, um zu verhindern, dass Ihre Methode von einer Unterklasse überschrieben oder versehentlich aufgerufen wird. Lassen Sie uns ein Beispiel sehen:

class A(object):
    def __test(self):
        print "I'm a test method in class A"

    def test(self):
        self.__test()

a = A()
a.test()
# a.__test() # This fails with an AttributeError
a._A__test() # Works! We can access the mangled name directly!

Ausgabe:

$ python test.py
I'm test method in class A
I'm test method in class A

Erstellen Sie nun eine Unterklasse B und passen Sie die Methode __test an

class B(A):
    def __test(self):
        print "I'm test method in class B"

b = B()
b.test()

Die Ausgabe lautet....

$ python test.py
I'm test method in class A

Wie wir gesehen haben, hat A.test() nicht die Methoden von B.__test() aufgerufen, wie man erwarten könnte. Tatsächlich ist dies aber das richtige Verhalten für __. Die beiden Methoden mit dem Namen __test() werden automatisch in _A__test() und _B__test() umbenannt, damit sie nicht versehentlich überschrieben werden. Wenn Sie eine Methode erstellen, die mit __ beginnt, bedeutet das, dass Sie nicht wollen, dass irgendjemand sie überschreiben kann, und dass Sie nur innerhalb ihrer eigenen Klasse auf sie zugreifen wollen.

Zwei Unterstriche am Anfang und am Ende:

Wenn wir eine Methode wie __this__ rufen Sie nicht an. Dies ist eine Methode, die Python aufrufen soll, nicht Sie. Schauen wir uns das mal an:

>>> name = "test string"
>>> name.__len__()
11
>>> len(name)
11

>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60

Es gibt immer einen Operator oder eine native Funktion, die diese magischen Methoden aufruft. Manchmal ist es nur ein Haken, den Python in bestimmten Situationen aufruft. Zum Beispiel __init__() wird bei der Erstellung des Objekts aufgerufen, nachdem __new__() wird aufgerufen, um die Instanz zu erstellen...

Nehmen wir ein Beispiel...

class FalseCalculator(object):

    def __init__(self, number):
        self.number = number

    def __add__(self, number):
        return self.number - number

    def __sub__(self, number):
        return self.number + number

number = FalseCalculator(20)
print number + 10      # 10
print number - 20      # 40

Weitere Einzelheiten finden Sie in der PEP-8-Leitfaden . Für weitere magische Methoden, siehe dieses PDF .

2 Stimmen

Nachdem ich diese Antwort selbst bearbeitet habe, bevorzuge ich stackoverflow.com/a/8689983/1048186

0 Stimmen

Was meinen Sie mit "Wie wir gesehen haben, hat A.test() die Methode B.__test() nicht aufgerufen" - wo haben Sie A.test() aufgerufen?

0 Stimmen

@variable Er meinte B.test(), ich habe es korrigiert!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X