Jakie jest znaczenie pojedynczego i podwójnego podkreślenia przed nazwą obiektu?

1291

Czy ktoś może wyjaśnić dokładne znaczenie wiodących znaków podkreślenia przed nazwą obiektu w Pythonie i różnicę między nimi?

Ponadto, czy to znaczenie pozostaje takie samo, czy przedmiotowy obiekt jest zmienną, funkcją, metodą itp.?

ivanleoncz
źródło
2
Świetna krótka odpowiedź z innego wątku: stackoverflow.com/a/8689983/911945
Anton Tarasenko

Odpowiedzi:

1154

Pojedynczy podkreślnik

Nazwy w klasie z wiodącym podkreśleniem mają po prostu wskazywać innym programistom, że atrybut lub metoda mają być prywatne. Jednak sama nazwa nie robi nic specjalnego.

Cytując PEP-8 :

_single_leading_underscore: słaby wskaźnik „do użytku wewnętrznego”. Np. from M import *Nie importuje obiektów, których nazwa zaczyna się znakiem podkreślenia.

Podwójny znak podkreślenia (zarządzanie nazwami)

Z dokumentacji Pythona :

Każdy identyfikator formy __spam(co najmniej dwa wiodące podkreślenia, co najmniej jeden końcowy podkreślnik) jest tekstowo zastąpiony _classname__spam, gdzie classnamejest obecna nazwa klasy z wiodącymi podkreśleniem (podkreśleniami). To zniekształcanie odbywa się bez względu na pozycję syntaktyczną identyfikatora, więc można go użyć do zdefiniowania instancji klasy-prywatnej i zmiennych klas, metod, zmiennych przechowywanych w globalsach, a nawet zmiennych przechowywanych w instancjach. prywatny dla tej klasy w instancjach innych klas.

I ostrzeżenie z tej samej strony:

Zmiana nazw ma na celu zapewnienie klasom łatwego sposobu definiowania „prywatnych” zmiennych i metod instancji, bez konieczności martwienia się o zmienne instancji zdefiniowane przez klasy pochodne lub przemieszanie ze zmiennymi instancji przez kod poza klasą. Należy pamiętać, że reguły zniekształcania mają na celu przede wszystkim uniknięcie wypadków; zdeterminowana dusza może nadal uzyskiwać dostęp lub modyfikować zmienną uważaną za prywatną.

Przykład

>>> 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!'}
Andrew Keeton
źródło
17
Co się stanie, jeśli zadeklarowana zostanie nazwa zmiennej z 2 znakami podkreślenia, których nie ma w klasie? To tylko zwykła zmienna, prawda?
Dhruv Ramani
5
jakie jest znaczenie __podwójnego podkreślenia jako nazwy zmiennej? jaka, __ = foo()
AJ
104
Ta odpowiedź jest bardzo myląca, ponieważ prowadzi czytelnika do przekonania, że ​​dunderscore służy do nadawania atrybutom instancji „superprywatności”. Tak nie jest , jak wyjaśnił tu Raymond Hettinger, który wyraźnie stwierdza, że ​​dunderscore jest niepoprawnie używany do oznaczania członków jako prywatnych, podczas gdy został zaprojektowany jako przeciwieństwo prywatnego.
Markus Meskanen
13
@MarkusMeskanen Nie zgadzam się, odpowiedź wyraźnie stwierdza użycie wyniku dunderscore do tworzenia instancji zmiennych i metod klasy-prywatnej. Podczas gdy wynik dunderscore został zaprojektowany w taki sposób, aby te metody i zmienne były łatwo zastępowane przez podklasy (upublicznianie), użycie wyniku dunderscore zachowuje prywatną instancję do użytku w tej klasie.
arewm
6
@MarkusMeskanen: Swoboda jest, aby podklasy używały tych samych nazw, co robi nadklasa bez zrzucania nadklasy - innymi słowy, nazwy dunderklasy stają się prywatne dla siebie.
Ethan Furman
311

Jak dotąd doskonałe odpowiedzi, ale brakuje niektórych ciekawostek. Pojedyncza wiodącym podkreślenia nie jest dokładnie tylko konwencja: jeśli używasz from foobar import *, a moduł foobarnie definiuje __all__listę nazwy importowane z modułu nie obejmują tych z wiodących podkreślenia. Powiedzmy, że jest to głównie konwencja, ponieważ ta sprawa jest dość niejasnym zakątkiem ;-).

Konwencja wiodącego podkreślenia jest szeroko stosowana nie tylko dla nazw prywatnych , ale także dla tego, co C ++ nazwałby chronionymi - na przykład, nazw metod, które są całkowicie przeznaczone do zastąpienia przez podklasy (nawet te, które należy zastąpić, ponieważ w klasa podstawowa one raise NotImplementedError! -) są często pojedynczymi wiodącymi znakami podkreślenia, które wskazują kodowi za pomocą instancji tej klasy (lub podklas), że wspomniane metody nie powinny być wywoływane bezpośrednio.

Na przykład, aby utworzyć kolejkę bezpieczną dla wątków z inną dyscypliną kolejkowania niż FIFO, jedna importuje kolejkę, podklasę kolejki. Kolejkę i zastępuje takie metody jak _geti _put; „kod klienta” nigdy nie wywołuje tych metod („przechwytujących”), ale raczej metody publiczne („organizujące”), takie jak puti get(jest to znane jako wzorzec projektowy metody szablonowej - patrz np. tutaj na interesującą prezentację opartą na filmie mojego wystąpienia na ten temat, z dodatkiem streszczenia transkryptu).

Edycja: Łącza wideo w opisie rozmów są teraz zepsute. Możesz znaleźć dwa pierwsze filmy tutaj i tutaj .

Alex Martelli
źródło
1
Jak więc zdecydować, czy użyć, _var_nameczy użyć var_name+ wykluczenia __all__?
endolith
3
@endolith Użyj wiodącego znaku podkreślenia, aby zasygnalizować czytnikowi twojego kodu, że prawdopodobnie nie powinien tego używać (np. ponieważ możesz to zmienić w wersji 2.0, a nawet 1.1); używaj jawnego, __all__gdy chcesz, aby moduł był from spam import *przyjazny (w tym w interaktywnym tłumaczu). Tak więc przez większość czasu odpowiedź jest obie .
abarnert,
@AlexMartelli Czy ta reguła związana z importem jest omawiana legalnie gdzieś w dokumentach lub gdzie indziej?
Vicrobot
1
Lubię analogię C ++. Po pierwsze, nie lubię, kiedy ludzie nazywają to _ prywatne . Najwyraźniej mówię o analogiach, ponieważ w Pythonie nic nie jest naprawdę prywatne . Podczas nurkowania w semantyce Powiedziałbym możemy krawat _do Javy chroniony od proctected „klas pochodnych i / lub wewnątrz tego samego pakietu” w pomocy Java. Zamień pakiet na moduł, ponieważ PEP8 mówi nam już, że _nie jest to tylko konwencja, gdy mówimy o *imporcie i masz go. I zdecydowanie __byłby równoważny prywatnej Javie, gdy mówimy o identyfikatorach w klasie.
Marius Mucenicu,
2
Chociaż jest to przyzwoita odpowiedź, jest również bardzo autopromocyjna.
Hybrid web dev
298

__foo__: to tylko konwencja, sposób, w jaki system Python może używać nazw, które nie będą powodować konfliktów z nazwami użytkowników.

_foo: to tylko konwencja, sposób dla programisty na wskazanie, że zmienna jest prywatna (cokolwiek to znaczy w Pythonie).

__foo: to ma prawdziwe znaczenie: interpreter zastępuje tę nazwę _classname__foojako sposób, aby nazwa nie nakładała się na podobną nazwę w innej klasie.

Żadna inna forma podkreślenia nie ma znaczenia w świecie Python.

W tych konwencjach nie ma różnicy między klasą, zmienną, globalną itp.

Ned Batchelder
źródło
6
Właśnie natknąłem się __fooi ciekawy. Jak może nakładać się na podobne nazwy metod z innymi klasami? Mam na myśli, że nadal musisz uzyskać do niego dostęp instance.__foo()(jeśli nie zostałby zmieniony przez tłumacza), prawda?
Bibhas Debnath,
81
Ten facet twierdzi, że from module import *nie importuje obiektów z prefiksem podkreślenia. Dlatego _foojest czymś więcej niż tylko konwencją.
dotancohen
4
@Bibhas: jeśli klasa podklasuje Bklasę Ai obie implementują foo(), to B.foo()zastępuje .foo()odziedziczone A. BDostęp do instancji będzie możliwy tylko za B.foo()pośrednictwem super(B).foo().
naught101
3
W przypadku __dunder__nazw niejawne wywołania pomijają słownik instancji, więc w niektórych przypadkach może to być coś więcej niż tylko konwencja nazewnictwa (patrz sekcja poświęcona specjalnej metodzie wyszukiwania w modelu danych).
wim
206

._variable jest półprywatny i przeznaczony tylko do konwencji

.__variablejest często błędnie uważany za superprywatny, a jego faktyczne znaczenie to po prostu namemangle, aby zapobiec przypadkowemu dostępowi [1]

.__variable__ jest zwykle zarezerwowany dla wbudowanych metod lub zmiennych

Nadal możesz uzyskać dostęp do .__mangledzmiennych, jeśli desperacko chcesz. Podwójne podkreślenia po prostu namemangles lub zmienia nazwę zmiennej na coś podobnegoinstance._className__mangled

Przykład:

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

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

t._b jest dostępny, ponieważ jest ukryty tylko przez konwencję

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

t .__ a nie został znaleziony, ponieważ już nie istnieje z powodu namemangling

>>> t._Test__a
'a'

Uzyskując dostęp instance._className__variablezamiast podwójnej nazwy podkreślenia, można uzyskać dostęp do ukrytej wartości

NickCSE
źródło
ale co jeśli „__a” była zmienna klasy, to nie można uzyskać do niego dostęp nawet z instrukcjami docs Pythona ..
Vitaliy Terziev
Czy możesz zaktualizować swoją odpowiedź o przykład podwójnego podkreślenia w odniesieniu do dziedziczenia?
zmienna
116

Pojedynczy podkreślnik na początku:

Python nie ma prawdziwych prywatnych metod. Zamiast tego jeden znak podkreślenia na początku nazwy metody lub atrybutu oznacza, że ​​nie powinieneś uzyskiwać dostępu do tej metody, ponieważ nie jest ona częścią interfejsu API.

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)

(Ten fragment kodu został pobrany z kodu źródłowego django: django / form / forms.py). W tym kodzie errorsjest własnością publiczną, ale metoda wywoływana przez tę właściwość _get_errors jest „prywatna”, więc nie powinieneś mieć do niej dostępu.

Na początek dwa podkreślenia:

Powoduje to wiele zamieszania. Nie należy go używać do tworzenia prywatnej metody. Należy go użyć, aby uniknąć zastąpienia metody przez podklasę lub przypadkowego dostępu do niej. Zobaczmy przykład:

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!

Wynik:

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

Teraz utwórz podklasę B i dostosuj metodę __test

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

b = B()
b.test()

Wyjście będzie ....

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

Jak widzieliśmy, A.test () nie wywołał metod B .__ test (), jak można się spodziewać. Ale w rzeczywistości jest to prawidłowe zachowanie dla __. Dwie metody o nazwie __test () są automatycznie przemianowane (zniekształcone) na _A__test () i _B__test (), aby przypadkowo nie zostały zastąpione. Kiedy tworzysz metodę zaczynającą się od __, oznacza to, że nie chcesz, aby ktokolwiek mógł ją zastąpić, i masz zamiar uzyskać do niej dostęp tylko z jej własnej klasy.

Dwa podkreślenia na początku i na końcu:

Kiedy widzimy metodę podobną __this__, nie nazywaj jej. Jest to metoda, którą ma wywoływać Python, a nie ty. Spójrzmy:

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

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

Zawsze istnieje operator lub natywna funkcja, która wywołuje te magiczne metody. Czasami jest to zwykłe wywołanie Pythona w określonych sytuacjach. Na przykład __init__()jest wywoływany, gdy obiekt jest tworzony po __new__()wywołaniu w celu utworzenia instancji ...

Weźmy przykład ...

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

Aby uzyskać więcej informacji, zobacz przewodnik PEP-8 . Więcej magicznych metod znajdziesz w tym pliku PDF .

PythonDev
źródło
1
Po samodzielnym zredagowaniu tej odpowiedzi wolę stackoverflow.com/a/8689983/1048186
JosiahYoder-deactive, z wyjątkiem ..
Co rozumiesz przez „Jak widzieliśmy, A.test () nie wywołał metod B .__ test ()” - gdzie nazwałeś A.test ()?
zmienna
18

Czasami masz coś, co wydaje się być krotką z wiodącym podkreśleniem, jak w

def foo(bar):
    return _('my_' + bar)

W tym przypadku chodzi o to, że _ () jest aliasem dla funkcji lokalizacji, która działa na tekście, aby umieścić go we właściwym języku itp. W zależności od ustawień regionalnych. Na przykład Sphinx to robi, a znajdziesz wśród importowanych

from sphinx.locale import l_, _

a w sphinx.locale, _ () jest przypisany jako alias niektórych funkcji lokalizacji.

Tim D.
źródło
11

Ponieważ tak wiele osób odnosi się do przemówienia Raymonda, ułatwię to , pisząc to, co powiedział:

Intencją podwójnych podkreśleń nie była prywatność. Chodziło o to, aby użyć go dokładnie tak

class Circle(object):

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

    def area(self):
        p = self.__perimeter()
        r = p / math.pi / 2.0
        return math.pi * r ** 2.0

    def perimeter(self):
        return 2.0 * math.pi * self.radius

    __perimeter = perimeter  # local reference


class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

W rzeczywistości jest to przeciwieństwo prywatności, chodzi o wolność. Dzięki temu Twoje podklasy mogą zastąpić jedną metodę, nie psując pozostałych .

Załóżmy, że nie prowadzą lokalne odniesienie perimeterw Circle. Teraz klasa pochodna Tirezastępuje implementację perimeterbez dotykania area. Kiedy dzwonisz Tire(5).area(), teoretycznie powinien on nadal być wykorzystywany Circle.perimeterdo obliczeń, ale w rzeczywistości używa Tire.perimeter, co nie jest zamierzonym zachowaniem. Dlatego potrzebujemy lokalnego odniesienia w Circle.

Ale dlaczego __perimeterzamiast _perimeter? Ponieważ _perimeterwciąż daje klasie pochodnej szansę na przesłonięcie:

class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

    _perimeter = perimeter

Podwójne podkreślenia mają mangowanie nazw, więc istnieje małe prawdopodobieństwo, że lokalne odwołanie w klasie nadrzędnej zostanie zastąpione w klasie pochodnej. w ten sposób „ sprawia, że ​​Twoje podklasy mogą zastąpić jedną metodę, nie psując pozostałych ”.

Jeśli twoja klasa nie zostanie odziedziczona lub zastąpienie metody niczego nie zepsuje, po prostu nie potrzebujesz __double_leading_underscore.

laike9m
źródło
Dziękuję, slajd nie wyświetlał się poprawnie, więc nie przestałem się zastanawiać, dlaczego mój kod zawiódł.
cgte
8

Jeśli naprawdę chcemy, aby zmienna była tylko do odczytu, IMHO najlepiej byłoby użyć property () z przekazanym tylko getterowi. Dzięki property () możemy mieć pełną kontrolę nad danymi.

class PrivateVarC(object):

    def get_x(self):
        pass

    def set_x(self, val):
        pass

    rwvar = property(get_p, set_p)  

    ronly = property(get_p) 

Rozumiem, że OP zadał nieco inne pytanie, ale ponieważ znalazłem inne pytanie z pytaniem „jak ustawić zmienne prywatne” oznaczone jako duplikat tego, pomyślałem o dodaniu tutaj tych dodatkowych informacji.

Dev Maha
źródło
7

Zgadzając się z https://dbader.org/blog/meaning-of-underscores-in-python

  • Single Leading Underscore (_var) : Konwencja nazewnictwa wskazująca, że ​​nazwa jest przeznaczona do użytku wewnętrznego. Zasadniczo nie jest egzekwowane przez interpreter Pythona (z wyjątkiem importowania symboli wieloznacznych) i ma służyć jedynie jako wskazówka dla programisty.
  • Pojedynczy końcowy znak podkreślenia (var_) : Używany konwencjonalnie, aby uniknąć konfliktów nazw ze słowami kluczowymi Python.
  • Double Leading Underscore (__ var) : wyzwala mangling nazwy, gdy jest używana w kontekście klasy. Wymuszone przez interpretera Pythona.
  • Podwójny wiodący i końcowy znak podkreślenia (__ var__) : Wskazuje specjalne metody zdefiniowane w języku Python. Unikaj tego schematu nazewnictwa dla własnych atrybutów.
  • Pojedynczy znak podkreślenia (_) : Czasami używany jako nazwa zmiennych tymczasowych lub nieistotnych („nie przejmuj się”). Także: Wynik ostatniego wyrażenia w REPL Pythona.
Feuda
źródło
5

Świetne odpowiedzi i wszystkie są poprawne. Podałem prosty przykład wraz z prostą definicją / znaczeniem.

Znaczenie:

some_variable --► jest publiczny, każdy może to zobaczyć.

_some_variable --► jest publiczny, każdy może to zobaczyć, ale jest to konwencja oznaczająca prywatność ... ostrzegająca, że Python nie wymusza żadnego egzekwowania.

__some_varaible --► Python zamienia nazwę zmiennej na _classname__some_varaible (zniekształcanie nazwy AKA) i zmniejsza / ukrywa widoczność i przypomina bardziej zmienną prywatną.

Mówiąc szczerze, zgodnie z dokumentacją Pythona

Zmienne instancji „Prywatne”, do których nie można uzyskać dostępu, chyba że z wnętrza obiektu, nie istnieją w Pythonie ”

Przykład:

class A():
    here="abc"
    _here="_abc"
    __here="__abc"


aObject=A()
print(aObject.here) 
print(aObject._here)
# now if we try to print __here then it will fail because it's not public variable 
#print(aObject.__here)
grepit
źródło
_ _some_varaible - .... i zmniejsza / ukrywa widoczność i jest bardziej jak zmienna prywatna. Nie, zmiana nazwy jest istotna, nie kryje metody.
AMC
4

Pojedyncze wiodące podkreślenia to konwencja. z punktu widzenia tłumacza nie ma różnicy, czy nazwy zaczynają się od pojedynczego podkreślenia, czy nie.

Podwójne natarcia i spływu podkreślenia służą do wbudowanej metodami, takimi jak __init__, __bool__itp

Podwójne wiodące podkreślenia bez końcowych odpowiedników są również konwencją, jednak metody klasowe zostaną zniekształcone przez interpretera. W przypadku zmiennych lub podstawowych nazw funkcji nie ma żadnej różnicy.

SilentGhost
źródło
3

Twoje pytanie jest dobre, nie dotyczy tylko metod. Funkcje i obiekty w modułach są często poprzedzone jednym znakiem podkreślenia i mogą być poprzedzone dwoma.

Ale nazwy __double_underscore nie są na przykład zniekształcone w modułach. To, co się dzieje, polega na tym, że nazwy rozpoczynające się od jednego (lub więcej) podkreślników nie są importowane, jeśli importujesz wszystko z modułu (z importu modułu *), a także nazwy wyświetlane w pomocy (module).

u0b34a0f6ae
źródło
1
Ponadto nazwy zaczynające się od jednego lub więcej podkreśleń, które mają dwa lub więcej końcowych podkreśleń, zachowują się jak każda inna nazwa.
Bentley4,
3

Oto prosty przykład ilustrujący, w jaki sposób właściwości podwójnego podkreślenia mogą wpływać na odziedziczoną klasę. Więc z następującą konfiguracją:

class parent(object):
    __default = "parent"
    def __init__(self, name=None):
        self.default = name or self.__default

    @property
    def default(self):
        return self.__default

    @default.setter
    def default(self, value):
        self.__default = value


class child(parent):
    __default = "child"

jeśli następnie utworzysz instancję potomną w REPL pytona, zobaczysz poniżej

child_a = child()
child_a.default            # 'parent'
child_a._child__default    # 'child'
child_a._parent__default   # 'parent'

child_b = child("orphan")
## this will show 
child_b.default            # 'orphan'
child_a._child__default    # 'child'
child_a._parent__default   # 'orphan'

Dla niektórych może to być oczywiste, ale zaskoczyło mnie to w znacznie bardziej złożonym środowisku

Marc
źródło
3

„Prywatne” zmienne instancji, do których nie można uzyskać dostępu, chyba że z wnętrza obiektu, nie istnieją w Pythonie. Istnieje jednak konwencja, po której następuje większość kodu w języku Python: nazwa poprzedzona znakiem podkreślenia (np. _Spam) powinna być traktowana jako niepubliczna część interfejsu API (niezależnie od tego, czy jest to funkcja, metoda czy element danych) . Należy to uznać za szczegół implementacji i może ulec zmianie bez powiadomienia.

odniesienie https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

aptro
źródło
1
_ jest znacznie bardziej podobny do na przykład wewnętrznego w c # niż prywatnego. Podwójne podkreślenie jest bardziej podobne do prywatnego niż podkreślenie jest prywatne, powiedziałbym.
Ini
2

Poznanie faktów _ i __ jest dość łatwe; inne odpowiedzi wyrażają je całkiem dobrze. Użycie jest znacznie trudniejsze do ustalenia.

Tak to widzę:

_

Powinien być używany do wskazania, że ​​funkcja nie jest do użytku publicznego, jak na przykład interfejs API. To i ograniczenie importu sprawiają, że zachowuje się podobnie jak internalw języku c #.

__

Powinien być używany, aby uniknąć kolizji nazw w hierarchii dziedziczenia i aby uniknąć późnego wiązania. Podobnie jak prywatny w c #.

==>

Jeśli chcesz wskazać, że coś nie jest do użytku publicznego, ale powinno działać jak protectedpożytek _. Jeśli chcesz wskazać, że coś nie jest do użytku publicznego, ale powinno działać jak privatepożytek __.

Jest to również cytat, który bardzo lubię:

Problem polega na tym, że autor klasy może słusznie pomyśleć „ta nazwa atrybutu / metody powinna być prywatna, dostępna tylko z definicji tej klasy” i stosować konwencję __private. Ale później użytkownik tej klasy może utworzyć podklasę, która zgodnie z prawem potrzebuje dostępu do tej nazwy. Tak więc albo nadklasa musi zostać zmodyfikowana (co może być trudne lub niemożliwe), albo kod podklasy musi używać ręcznie zniekształconych nazw (które są co najmniej brzydkie i kruche).

Moim zdaniem problem polega na tym, że jeśli nie ma IDE, który ostrzegałby Cię podczas nadpisywania metod, znalezienie błędu może zająć trochę czasu, jeśli przypadkowo zastąpisz metodę z klasy podstawowej.

Invader
źródło