Klasa „prywatna” (implementacyjna) w Pythonie


107

Piszę mały moduł Pythona składający się z dwóch części:

  • niektóre funkcje definiujące interfejs publiczny,
  • klasa implementacji używana przez powyższe funkcje, ale która nie ma znaczenia poza modułem.

Na początku zdecydowałem się „ukryć” tę klasę implementacji, definiując ją wewnątrz funkcji, która jej używa, ale to utrudnia czytelność i nie może być używane, jeśli wiele funkcji ponownie używa tej samej klasy.

Czy zatem oprócz komentarzy i ciągów dokumentów istnieje mechanizm oznaczania klasy jako „prywatnej” lub „wewnętrznej”? Zdaję sobie sprawę z mechanizmu podkreślenia, ale jak rozumiem dotyczy to tylko zmiennych, nazw funkcji i metod.

Odpowiedzi:


175

Użyj pojedynczego podkreślenia przedrostka:

class _Internal:
    ...

To jest oficjalna konwencja Pythona dotycząca symboli „wewnętrznych”; „from module import *” nie importuje obiektów z przedrostkiem podkreślonym.

Edycja: odniesienie do konwencji pojedynczego podkreślenia


3
Nie znałem zasady podkreślenia rozciągniętej na zajęcia. Nie chcę zaśmiecać mojej przestrzeni nazw podczas importowania, więc właśnie tego szukałem. Dzięki!
oparisy

1
Ponieważ twierdzisz, że jest to „oficjalna” konwencja języka Python, byłoby miło, gdyby zawierał link. Inny post mówi, że wszystko jest oficjalne i zawiera odnośnik do dokumentacji.
flodin

11
python.org/dev/peps/pep-0008 - _single_leading_underscore: słaby wskaźnik „użytku wewnętrznego”. Np. „From M import *” nie importuje obiektów, których nazwa zaczyna się od podkreślenia. - Zajęcia do użytku wewnętrznego mają wiodące podkreślenie
Miles

2
Wiodącym podkreśleniem jest konwencja oznaczania rzeczy jako wewnętrznych, „nie zadzieraj z tym”, podczas gdy wszystko jest bardziej dla modułów zaprojektowanych do używania z „importem z M *”, niekoniecznie sugerując, że użytkownicy modułu nie powinni dotykać ta klasa.
Miles

65

W skrócie:

  1. Nie możesz wymusić prywatności . W Pythonie nie ma prywatnych klas / metod / funkcji. Przynajmniej nie ścisłej prywatności, jak w innych językach, takich jak Java.

  2. Możesz tylko wskazać / zasugerować prywatność . To jest zgodne z konwencją. Konwencja Pythona dotycząca oznaczania klasy / funkcji / metody jako prywatnej polega na poprzedzeniu jej znakiem _ (podkreślenie). Na przykład def _myfunc()lub class _MyClass:. Możesz również utworzyć pseudo-prywatność, poprzedzając metodę dwoma podkreśleniami (np __foo. :) . Nie możesz uzyskać bezpośredniego dostępu do metody, ale nadal możesz ją wywołać za pomocą specjalnego prefiksu przy użyciu nazwy klasy (np _classname__foo. :) . Więc najlepsze, co możesz zrobić, to wskazać / zasugerować prywatność, a nie ją egzekwować.

Pod tym względem Python jest podobny do perla. Parafrazując słynne zdanie o prywatności z książki Perla, filozofia jest taka, że ​​powinieneś trzymać się z dala od salonu, ponieważ nie zostałeś zaproszony, a nie dlatego, że jest broniony strzelbą.

Po więcej informacji:


W odpowiedzi na # 1 możesz w pewnym sensie wymusić prywatność metod. Użycie podwójnego podkreślenia, takiego jak __method (self), sprawi, że będzie niedostępny poza klasą. Ale można to obejść, wywołując ją w stylu Foo () ._ Foo__method (). Myślę, że po prostu zmienia nazwę na coś bardziej ezoterycznego.
Evan Fosmark

1
Ten cytat jest dokładny.
Paul Draper,


10

Wzór, którego czasami używam, to:

Zdefiniuj klasę:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Utwórz instancję klasy, nadpisując nazwę klasy:

x = x()

Zdefiniuj symbole ujawniające funkcjonalność:

doThis = x.doThis
doThat = x.doThat

Usuń samą instancję:

del x

Teraz masz moduł, który ujawnia tylko twoje funkcje publiczne.


2
Zajęło mi chwilę, aby zrozumieć cel / funkcję „nadpisywania nazwy klasy”, ale kiedy to zrobiłem, dostałem szeroki uśmiech. Nie jestem pewien, kiedy go użyję. :)
Zach Young

Czy jest jakaś nazwa tej techniki?
Bishwas Mishra


4

Aby rozwiązać kwestię konwencji projektowych, i jak powiedział Christopher, w Pythonie nie ma czegoś takiego jak „prywatny”. Może się to wydawać dziwne dla kogoś, kto pochodzi z C / C ++ (tak jak ja jakiś czas temu), ale w końcu prawdopodobnie zdasz sobie sprawę, że przestrzeganie konwencji wystarczy.

Widzenie czegoś z podkreśleniem z przodu powinno być wystarczająco dobrą wskazówką, aby nie używać tego bezpośrednio. Jeśli obawiasz się zaśmiecania help(MyClass)danych wyjściowych (na co wszyscy patrzą, szukając sposobu użycia klasy), podkreślone atrybuty / klasy nie są tam zawarte, więc w końcu zostanie opisany interfejs „publiczny”.

Dodatkowo, posiadanie wszystkiego, co publiczne, ma swoje własne niesamowite korzyści, na przykład, możesz testować jednostkowo prawie wszystko z zewnątrz (czego nie możesz zrobić z prywatnymi konstrukcjami C / C ++).


4

Użyj dwóch podkreślników, aby poprzedzić nazwy „prywatnych” identyfikatorów. W przypadku klas w module użyj pojedynczego początkowego podkreślenia, a nie zostaną zaimportowane przy użyciu opcji „from module import *”.

class _MyInternalClass:
    def __my_private_method:
        pass

(W Pythonie nie ma czegoś takiego jak prawdziwe „prywatne”. Na przykład Python po prostu automatycznie zmienia nazwy członków klasy z podwójnymi podkreśleniami na __clssname_mymember. Tak naprawdę, jeśli znasz zniekształconą nazwę, i tak możesz użyć jednostki „prywatnej” . Zobacz tutaj. Oczywiście, jeśli chcesz, możesz ręcznie importować klasy „wewnętrzne”).


Dlaczego dwa _? Jeden wystarczy.
S.Lott

Pojedynczy znak podkreślenia jest wystarczający, aby uniemożliwić importowi importowanie klas i funkcji. Potrzebujesz dwóch podkreśleń, aby wywołać funkcję zniekształcania nazw w Pythonie. Powinien być jaśniejszy; Redagowałem.
chroder

Teraz kontynuacja. Dlaczego nazywasz zniekształcenie? Jaka jest możliwa korzyść z tego?
S.Lott

5
Możliwa korzyść jest irytująca innych programistów, którzy potrzebują dostępu do tych zmiennych :)
Richard Levasseur
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.