W razie wątpliwości pozostaw to „publiczne” - to znaczy nie dodawaj niczego, co mogłoby zasłaniać nazwę swojego atrybutu. Jeśli masz klasę z jakąś wartością wewnętrzną, nie przejmuj się tym. Zamiast pisać:
class Stack(object):
def __init__(self):
self.__storage = [] # Too uptight
def push(self, value):
self.__storage.append(value)
napisz to domyślnie:
class Stack(object):
def __init__(self):
self.storage = [] # No mangling
def push(self, value):
self.storage.append(value)
To z pewnością kontrowersyjny sposób robienia rzeczy. Nowicjusze w Pythonie po prostu go nienawidzą, a nawet niektórzy starzy faceci w Pythonie gardzą tym domyślnym - ale i tak jest to ustawienie domyślne, więc naprawdę polecam ci go przestrzegać, nawet jeśli czujesz się nieswojo.
Jeśli naprawdę chcesz wysłać wiadomość „Nie możesz tego dotknąć!” użytkownikom, zwykle poprzedza się zmienną jednym podkreśleniem. To tylko konwencja, ale ludzie ją rozumieją i podwójnie ostrożnie obchodzą się z takimi rzeczami:
class Stack(object):
def __init__(self):
self._storage = [] # This is ok but pythonistas use it to be relaxed about it
def push(self, value):
self._storage.append(value)
Może to być również przydatne, aby uniknąć konfliktu między nazwami właściwości i nazwami atrybutów:
class Person(object):
def __init__(self, name, age):
self.name = name
self._age = age if age >= 0 else 0
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age >= 0:
self._age = age
else:
self._age = 0
A co z podwójnym podkreśleniem? Cóż, magia podwójnego podkreślenia jest używana głównie w celu uniknięcia przypadkowego przeładowania metod i konfliktów nazw z atrybutami nadklas . Może to być całkiem przydatne, jeśli napiszesz klasę, która ma być wielokrotnie przedłużana.
Jeśli chcesz go użyć do innych celów, możesz, ale nie jest to ani zwyczajne, ani zalecane.
EDYCJA : Dlaczego tak jest? Cóż, zwykły styl Pythona nie kładzie nacisku na prywatność - wręcz przeciwnie! Powodów jest wiele - większość z nich jest kontrowersyjna ... Zobaczmy kilka z nich.
Python ma właściwości
Większość języków OO stosuje obecnie odwrotne podejście: to, czego nie powinno się używać, nie powinno być widoczne, więc atrybuty powinny być prywatne. Teoretycznie dałoby to łatwiejsze w zarządzaniu, mniej powiązane ze sobą klasy, ponieważ nikt nie zmieniałby lekkomyślnie wartości wewnątrz obiektów.
Jednak nie jest to takie proste. Na przykład klasy Java mają wiele atrybutów i metod pobierających, które pobierają tylko wartości i metody ustawiające, które po prostu ustawiają wartości. Powiedzmy, że potrzebujesz, powiedzmy, siedmiu linii kodu, aby zadeklarować pojedynczy atrybut - który programista Pythona powiedziałby, że jest niepotrzebnie złożony. Ponadto w praktyce po prostu piszesz całą masę kodu, aby uzyskać jedno pole publiczne, ponieważ możesz zmienić jego wartość za pomocą metod pobierających i ustawiających.
Dlaczego więc stosować tę domyślną zasadę prywatności? Po prostu ustaw domyślnie swoje atrybuty jako publiczne. Oczywiście jest to problematyczne w Javie, ponieważ jeśli zdecydujesz się dodać walidację do swojego atrybutu, wymagałoby to zmiany wszystkich
person.age = age;
w swoim kodzie, powiedzmy,
person.setAge(age);
setAge()
istota:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
this.age = 0;
}
}
Tak więc w Javie (i innych językach) domyślnie i tak są używane metody pobierające i ustawiające, ponieważ ich pisanie może być denerwujące, ale może zaoszczędzić dużo czasu, jeśli znajdziesz się w opisanej przeze mnie sytuacji.
Jednak nie musisz tego robić w Pythonie, ponieważ Python ma właściwości. Jeśli masz tę klasę:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
a następnie decydujesz się na walidację wieku, nie musisz zmieniać person.age = age
fragmentów swojego kodu. Po prostu dodaj właściwość (jak pokazano poniżej)
class Person(object):
def __init__(self, name, age):
self.name = name
self._age = age if age >= 0 else 0
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age >= 0:
self._age = age
else:
self._age = 0
Jeśli możesz to zrobić i nadal używać person.age = age
, dlaczego miałbyś dodawać pola prywatne oraz pobierające i ustawiające?
(Zobacz też, że Python to nie Java i ten artykuł o szkodliwości używania metod pobierających i ustawiających ).
Wszystko i tak jest widoczne - a próba ukrycia tylko komplikuje pracę
Nawet w językach, w których istnieją prywatne atrybuty, możesz uzyskać do nich dostęp za pośrednictwem jakiejś biblioteki refleksji / introspekcji. A ludzie robią to dużo, w ramach i dla rozwiązywania pilnych potrzeb. Problem polega na tym, że biblioteki introspekcji są po prostu trudnym sposobem robienia tego, co można zrobić z atrybutami publicznymi.
Ponieważ Python jest bardzo dynamicznym językiem, dodawanie tego obciążenia do swoich klas przyniesie po prostu efekt przeciwny do zamierzonego.
Problemu nie da się zobaczyć - trzeba go zobaczyć
Dla Pythonisty hermetyzacja to nie niemożność zobaczenia wewnętrznych elementów klas, ale możliwość uniknięcia patrzenia na nie. Chodzi mi o to, że hermetyzacja jest właściwością komponentu, która pozwala na jej użycie bez obawy użytkownika o wewnętrzne szczegóły. Jeśli możesz użyć komponentu bez zawracania sobie głowy jego implementacją, to jest on hermetyzowany (zdaniem programisty Pythona).
Teraz, jeśli napisałeś swoją klasę w taki sposób, że możesz jej używać bez zastanawiania się nad szczegółami implementacji, nie ma problemu, jeśli z jakiegoś powodu chcesz zajrzeć do klasy. Chodzi o to, że twoje API powinno być dobre, a reszta to szczegóły.
Guido tak powiedział
Cóż, to nie jest kontrowersyjne: tak naprawdę powiedział . (Poszukaj „otwartego kimona”).
To jest kultura
Tak, jest kilka powodów, ale nie ma krytycznego powodu. Jest to głównie kulturowy aspekt programowania w Pythonie. Szczerze mówiąc, może być też inaczej - ale tak nie jest. Możesz również równie łatwo zapytać na odwrót: dlaczego niektóre języki domyślnie używają atrybutów prywatnych? Z tego samego powodu, co w przypadku praktyki Pythona: ponieważ jest to kultura tych języków, a każdy wybór ma zalety i wady.
Ponieważ ta kultura już istnieje, zaleca się jej przestrzeganie. W przeciwnym razie denerwują Cię programiści Pythona, którzy każą ci usunąć __
z kodu, gdy zadasz pytanie w przepełnieniu stosu :)
Po pierwsze - co to jest zniekształcanie nazw?
Zniekształcanie nazw jest wywoływane, gdy znajdujesz się w definicji klasy i używasz
__any_name
lub__any_name_
, to znaczy dwóch (lub więcej) wiodących znaków podkreślenia i co najwyżej jednego końcowego podkreślenia.I teraz:
Pozornym użyciem jest uniemożliwienie podklasom używania atrybutu używanego przez klasę.
Potencjalną wartością jest unikanie kolizji nazw z podklasami, które chcą przesłonić zachowanie, tak aby funkcje klasy nadrzędnej działały zgodnie z oczekiwaniami. Jednak przykład w dokumentacji Pythona nie jest zastępowalny przez Liskov i nie przychodzą mi do głowy żadne przykłady, w których uznałem to za przydatne.
Wadą jest to, że zwiększa obciążenie poznawcze do odczytu i zrozumienia podstawy kodu, a zwłaszcza podczas debugowania, w którym widzisz nazwę podwójnego podkreślenia w źródle i zniekształconą nazwę w debugerze.
Moje osobiste podejście polega na celowym unikaniu tego. Pracuję na bardzo dużej bazie kodu. Rzadkie zastosowania tego wystają jak ból kciuka i nie wydają się uzasadnione.
Musisz być tego świadomy, aby wiedzieć, kiedy to widzisz.
PEP 8
PEP 8 , przewodnik po stylach bibliotek standardowych w Pythonie, mówi obecnie (w skrócie):
Jak to działa?
Jeśli w definicji klasy wstawisz dwa podkreślenia (bez kończenia podwójnych podkreśleń), nazwa zostanie zniekształcona, a na obiekcie zostanie dodany znak podkreślenia, po którym nastąpi nazwa klasy:
Zwróć uwagę, że nazwy zostaną zniekształcone tylko podczas analizowania definicji klasy:
Ponadto nowi w Pythonie czasami mają problemy ze zrozumieniem, co się dzieje, gdy nie mogą ręcznie uzyskać dostępu do nazwy, którą widzą w definicji klasy. To nie jest silny powód przeciwko temu, ale warto to rozważyć, jeśli masz uczących się odbiorców.
Jedno podkreślenie?
Kiedy zamierzam, aby użytkownicy trzymali ręce z dala od atrybutu, zwykle używam tylko jednego podkreślenia, ale to dlatego, że w moim modelu mentalnym podklasy miałyby dostęp do nazwy (którą zawsze mają, ponieważ mogą łatwo zauważyć i tak zniekształcona nazwa).
Gdybym przeglądał kod, który używa
__
prefiksu, zapytałbym, dlaczego wywołują zniekształcanie nazw i czy nie mogliby równie dobrze poradzić sobie z pojedynczym podkreśleniem, pamiętając, że jeśli podklasy wybierają te same nazwy dla klasy i class mimo to nastąpi kolizja nazw.źródło
Nie powiedziałbym, że praktyka tworzy lepszy kod. Modyfikatory widoczności tylko odwracają uwagę od wykonywanego zadania, a jako efekt uboczny wymuszają używanie interfejsu zgodnie z zamierzeniami. Ogólnie rzecz biorąc, wymuszanie widoczności uniemożliwia programistom zepsucie rzeczy, jeśli nie przeczytali poprawnie dokumentacji.
O wiele lepszym rozwiązaniem jest trasa, którą zachęca Python: Twoje klasy i zmienne powinny być dobrze udokumentowane, a ich zachowanie jasne. Źródło powinno być dostępne. Jest to znacznie bardziej rozszerzalny i niezawodny sposób pisania kodu.
Moja strategia w Pythonie jest taka:
Przede wszystkim powinno być jasne, co robi wszystko. Udokumentuj to, jeśli ktoś inny będzie go używał. Udokumentuj to, jeśli chcesz, aby było przydatne za rok.
Na marginesie, w tych innych językach powinieneś korzystać z opcji chronionych : nigdy nie wiesz, że Twoja klasa może zostać później odziedziczona i do czego może zostać użyta. Najlepiej jest chronić tylko te zmienne, co do których jesteś pewien, że nie mogą lub nie powinny być używane przez obcy kod.
źródło
Nie powinieneś zaczynać od prywatnych danych i upubliczniać je w razie potrzeby. Zamiast tego powinieneś zacząć od określenia interfejsu twojego obiektu. Oznacza to, że powinieneś zacząć od ustalenia, co widzi świat (rzeczy publiczne), a następnie dowiedzieć się, jakie prywatne rzeczy są niezbędne, aby tak się stało.
Inny język utrudnia upublicznienie tego, co kiedyś było publiczne. Tzn. Zepsuję dużo kodu, jeśli ustawię moją zmienną jako prywatną lub chronioną. Ale w przypadku właściwości w Pythonie tak nie jest. Raczej mogę zachować ten sam interfejs nawet przy zmianie układu danych wewnętrznych.
Różnica między _ a __ polega na tym, że Python faktycznie próbuje wymusić to drugie. Oczywiście nie jest to bardzo trudne, ale to utrudnia. Mając _ tylko mówi innym programistom, jaki jest zamiar, mogą zignorować na własne ryzyko. Ale ignorowanie tej zasady jest czasami pomocne. Przykłady obejmują debugowanie, tymczasowe hacki i pracę z kodem innej firmy, który nie był przeznaczony do użytku w sposób, w jaki go używasz.
źródło
Jest już na to wiele dobrych odpowiedzi, ale mam zamiar zaproponować inną. Jest to również częściowo odpowiedź dla ludzi, którzy powtarzają, że podwójne podkreślenie nie jest prywatne (tak naprawdę jest).
Jeśli spojrzysz na Javę / C #, oba mają prywatne / chronione / publiczne. Wszystkie te są konstrukcjami w czasie kompilacji . Są one egzekwowane dopiero w momencie kompilacji. Gdybyś miał użyć odbicia w Javie / C #, mógłbyś łatwo uzyskać dostęp do metody prywatnej.
Teraz za każdym razem, gdy wywołujesz funkcję w Pythonie, z natury korzystasz z odbicia. Te fragmenty kodu są takie same w Pythonie.
Składnia „kropki” jest tylko cukrem składniowym dla drugiego fragmentu kodu. Głównie dlatego, że używanie getattr jest już brzydkie przy tylko jednym wywołaniu funkcji. Stamtąd jest tylko gorzej.
W związku z tym nie może istnieć prywatna wersja Java / C #, ponieważ Python nie kompiluje kodu. Java i C # nie mogą sprawdzić, czy funkcja jest prywatna czy publiczna w czasie wykonywania, ponieważ ta informacja zniknęła (i nie ma wiedzy o tym, skąd funkcja jest wywoływana).
Teraz, mając te informacje, zniekształcenie nazwy podwójnego podkreślenia jest najbardziej sensowne dla osiągnięcia „prywatności”. Teraz, gdy funkcja jest wywoływana z instancji „self” i zauważa, że zaczyna się od „__”, po prostu wykonuje tam zniekształcenie nazwy. To po prostu więcej cukru syntaktycznego. Ten cukier składniowy dopuszcza odpowiednik słowa „prywatny” w języku, który używa odbicia jedynie w celu uzyskania dostępu do elementów danych.
Zastrzeżenie: nigdy nie słyszałem, aby ktokolwiek z programistów Pythona powiedział coś takiego. Prawdziwym powodem braku „prywatnego” jest kultura, ale zauważysz także, że większość języków skryptowych / interpretowanych nie ma języka prywatnego. Ściśle egzekwowalny prywatny nie jest praktyczny w żadnym wypadku, z wyjątkiem czasu kompilacji.
źródło
Po pierwsze: dlaczego chcesz ukryć swoje dane? Dlaczego to takie ważne?
W większości przypadków nie chcesz tego robić, ale robisz to, ponieważ robią to inni.
Jeśli naprawdę naprawdę nie chcesz, aby ludzie czegoś używali, dodaj przed sobą jeden podkreślenie. To wszystko ... Pythonistas wiedzą, że rzeczy z jednym podkreśleniem nie są gwarantowane za każdym razem i mogą ulec zmianie bez Twojej wiedzy.
Tak właśnie żyjemy i nie przeszkadza nam to.
Użycie dwóch podkreśleń sprawi, że Twoja klasa będzie tak źle podklasowa, że nawet ty nie będziesz chciał pracować w ten sposób.
źródło
Wybrana odpowiedź dobrze radzi sobie z wyjaśnieniem, w jaki sposób właściwości eliminują potrzebę prywatnych atrybutów , ale dodałbym również, że funkcje na poziomie modułu eliminują potrzebę prywatnych metod .
Jeśli zmienisz metodę w funkcję na poziomie modułu, usuniesz możliwość zastąpienia jej przez podklasy. Przeniesienie niektórych funkcji na poziom modułu jest bardziej Pythonowe niż próba ukrycia metod z zniekształcaniem nazw.
źródło
Poniższy fragment kodu wyjaśni wszystkie różne przypadki:
bez podkreślenia (a)
drukowanie wszystkich poprawnych atrybutów obiektu testowego
Tutaj możesz zobaczyć, że nazwa __a została zmieniona na _Test__a, aby zapobiec nadpisaniu tej zmiennej przez jakąkolwiek podklasę. Ta koncepcja jest znana w Pythonie jako „zniekształcanie nazw”. Możesz uzyskać dostęp do tego w następujący sposób:
Podobnie, w przypadku _a, zmienna ma po prostu powiadomić dewelopera, że powinna być używana jako zmienna wewnętrzna tej klasy, interpreter Pythona nic nie zrobi, nawet jeśli uzyskasz do niego dostęp, ale nie jest to dobra praktyka.
zmienna może być dostępna z dowolnego miejsca, jest podobna do publicznej zmiennej klasy.
Mam nadzieję, że odpowiedź ci pomogła :)
źródło
Na pierwszy rzut oka powinno być to samo, co w przypadku innych języków (pod „innymi” mam na myśli Javę lub C ++), ale tak nie jest.
W Javie ustawiłeś wszystkie zmienne jako prywatne, które nie powinny być dostępne na zewnątrz. Jednocześnie w Pythonie nie można tego osiągnąć, ponieważ nie ma „prywatności” (jak mówi jedna z zasad Pythona - „Wszyscy jesteśmy dorośli”). Tak więc podwójne podkreślenie oznacza tylko „Chłopaki, nie używaj tego pola bezpośrednio”. To samo znaczenie ma pojedyncze podkreślenie, które jednocześnie nie powoduje bólu głowy, gdy trzeba dziedziczyć z rozważanej klasy (tylko przykład możliwego problemu spowodowanego podwójnym podkreśleniem).
Tak więc radziłbym domyślnie używać pojedynczego podkreślenia dla członków „prywatnych”.
źródło
„W przypadku wątpliwości, czy zmienna powinna być prywatna, czy chroniona, lepiej wybrać opcję prywatną”. - tak, to samo dotyczy Pythona.
Niektóre odpowiedzi mówią o „konwencjach”, ale nie podawaj linków do tych konwencji. W autorytatywnym przewodniku po Pythonie, PEP 8 wyraźnie stwierdza:
W innych odpowiedziach uwzględniono rozróżnienie między publicznym i prywatnym oraz zniekształcanie nazw w Pythonie. Z tego samego linku
źródło
# PRZYKŁADOWY PROGRAM ZMIANY NAZW Pythona
źródło