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.?
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.?
Odpowiedzi:
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.
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
, gdzieclassname
jest 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ą.
>>> 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!'}
__
podwójnego podkreślenia jako nazwy zmiennej? jaka, __ = foo()
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ł foobar
nie 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 _get
i _put
; „kod klienta” nigdy nie wywołuje tych metod („przechwytujących”), ale raczej metody publiczne („organizujące”), takie jak put
i 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 .
_var_name
czy użyć var_name
+ wykluczenia __all__
?
__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 .
_
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.
__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__foo
jako 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.
__foo
i 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?
from module import *
nie importuje obiektów z prefiksem podkreślenia. Dlatego _foo
jest czymś więcej niż tylko konwencją.
B
klasę A
i obie implementują foo()
, to B.foo()
zastępuje .foo()
odziedziczone A
. B
Dostęp do instancji będzie możliwy tylko za B.foo()
pośrednictwem super(B).foo()
.
__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).
._variable
jest półprywatny i przeznaczony tylko do konwencji
.__variable
jest 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 .__mangled
zmiennych, 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__variable
zamiast podwójnej nazwy podkreślenia, można uzyskać dostęp do ukrytej wartości
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 errors
jest 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 .
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.
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 perimeter
w Circle
. Teraz klasa pochodna Tire
zastępuje implementację perimeter
bez dotykania area
. Kiedy dzwonisz Tire(5).area()
, teoretycznie powinien on nadal być wykorzystywany Circle.perimeter
do obliczeń, ale w rzeczywistości używa Tire.perimeter
, co nie jest zamierzonym zachowaniem. Dlatego potrzebujemy lokalnego odniesienia w Circle.
Ale dlaczego __perimeter
zamiast _perimeter
? Ponieważ _perimeter
wciąż 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
.
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.
Zgadzając się z https://dbader.org/blog/meaning-of-underscores-in-python
Ś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)
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.
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).
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
„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
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 internal
w 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 protected
pożytek _
. Jeśli chcesz wskazać, że coś nie jest do użytku publicznego, ale powinno działać jak private
poż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.