Definiowanie funkcji modułu prywatnego w Pythonie

238

Według http://www.faqs.org/docs/diveintopython/fileinfo_private.html :

Podobnie jak większość języków, Python ma pojęcie elementów prywatnych:

  • Funkcje prywatne, których nie można wywołać spoza modułu

Jeśli jednak zdefiniuję dwa pliki:

#a.py
__num=1

i:

#b.py
import a
print a.__num

po uruchomieniu b.pydrukuje się 1bez żadnego wyjątku. Czy diveintopython jest zły, czy coś źle zrozumiałem? A czy jest jakiś sposób, aby nie definiować funkcje modułu jako prywatny?

olamundo
źródło
Nie jest tak, że diveintopython jest zły, ale w ich przykładzie: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo () jest instancją klasy. Co daje ten wyjątek, gdy używasz podwójnego podkreślenia. Podczas gdy w twoim przypadku nie stworzyłeś klasy, po prostu stworzyłeś moduł. Zobacz także: stackoverflow.com/questions/70528/…
Homero Esmeraldo

Odpowiedzi:

323

W Pythonie „prywatność” zależy od poziomu „zgody dorosłych” - nie można tego narzucić (bardziej niż w prawdziwym życiu ;-). Jeden wiodący znak podkreślenia oznacza, że ​​nie powinieneś uzyskiwać do niego dostępu „z zewnątrz” - dwa wiodące znaki podkreślenia (bez podkreślenia końcowego) przenoszą komunikat jeszcze mocniej ... ale ostatecznie zależy to od społeczności konwencja i konsensus: introspekcja Pythona jest na tyle silna, że ​​nie możesz kajdanek każdego innego programisty na świecie, aby uszanować twoje życzenia.

((Btw, jeśli jest to ściśle utrzymywane w tajemnicy, tak samo odnosi się do C ++: z większością kompilatorów, prosta #define private publiclinia przed #includeing swoje .h! Plik jest potrzebny dla przebiegłych koderów zrobić skrót swojej „prywatności” ... -) )

Alex Martelli
źródło
82
Twoja notatka na temat C ++ jest niepoprawna. Używając #define private public zmieniasz kod, który jest wysyłany do kompilatora, gdzie odbywa się mangowanie nazwy.
nosorożec przeznacz
14
Również manipulowanie w C ++ jest niejasne, ale nie jest tajemnicą. Możesz także „introspekcjonować” plik binarny stworzony przez C ++. OT, przepraszam.
Prof. Falken
47
Jako aktualizacja @ rhinoinrepose, nie jest to po prostu niepoprawne, jest to niezdefiniowane zachowanie zgodnie ze standardem w celu przedefiniowania słowa kluczowego za pomocą makra preprocesora.
Cory Kramer,
3
@AlexMartelli nie jest static void foo()tak prywatny, jak to możliwe. Jest on przynajmniej ukryty dla linkera, a funkcję można całkowicie usunąć przez wstawienie.
user877329,
3
W prawdziwym życiu ludzie są ścigani za łamanie prawa
Lay González
288

Pomiędzy szeregowymi szeregowymi a prywatnymi modułowymi może być zamieszanie .

A Moduł prywatne zaczyna się od jednego podkreślenia
taki element nie zostanie skopiowany razem podczas korzystania z from <module_name> import *formularza polecenia importu; jest jednak importowany, jeśli używa się import <moudule_name>składni ( patrz odpowiedź Bena Wilhelma )
Po prostu usuń jeden znak podkreślenia z a .__ przykładu pytania i nie będzie on wyświetlany w modułach, które importują a.py przy użyciu from a import *składni.

A klasa prywatne rozpoczyna się dwoma podkreśleniami (aka Dunder czyli d-ouble pod-score)
Taka zmienna ma nazwę „zniekształcone” zawierać nazwę klasy itd.
To może nadal być dostępne poza logiką klasowej, poprzez zniekształconej nazwy.
Chociaż zmiana nazwy może służyć jako łagodne urządzenie zapobiegające nieautoryzowanemu dostępowi, jej głównym celem jest zapobieganie możliwym kolizjom nazw z członkami klas klas przodków. Zobacz zabawne, ale dokładne odniesienie Alexa Martellego do wyrażających zgodę dorosłych, gdy opisuje konwencję stosowaną w odniesieniu do tych zmiennych.

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>
mjv
źródło
Cóż, TIL. Jest jednak jakiś powód, dla którego nie wymuszają poziomu modułu __private_function? Wpadłem na to i popełniłem z tego powodu błędy.
Święty Mikołaj
Dziękuję za miłe słowa @Terrabits, ale chętnie stoję po drugiej stronie Alexa (daleko w tyle!) We wszystkich sprawach „Python”. Co więcej, jego odpowiedzi są zazwyczaj bardziej zwięzłe i pełne humoru, przy zachowaniu wysokiego poziomu autorytatywności, biorąc pod uwagę duży wkład Alexa w język i społeczność.
mjv
1
@mjv To było takie pomocne wyjaśnienie! Dziękuję Ci! Przez pewien czas byłem dość zaskoczony tym zachowaniem. Chciałbym, aby wybór polegał na rzuceniu jakiegoś błędu innego niż AttributeError, jeśli próbowałeś uzyskać bezpośredni dostęp do klasy prywatnej; być może „PrivateAccessError” lub coś bardziej precyzyjnego / pomocnego. (Ponieważ uzyskanie błędu, że nie ma atrybutu, nie jest tak naprawdę prawdą).
HFBrowning
82

Na to pytanie nie udzielono pełnej odpowiedzi, ponieważ prywatność modułu nie jest czysto konwencjonalna, a ponieważ importowanie może, ale nie musi, rozpoznać prywatność modułu, w zależności od tego, w jaki sposób jest używany.

Jeśli zdefiniujesz prywatne nazwy w module, nazwy te zostaną zaimportowane do dowolnego skryptu korzystającego ze składni „import nazwa_modułu”. Tak więc, zakładając, że poprawnie zdefiniowałeś w swoim przykładzie moduł private, _num, w a.py, tak jak ...

#a.py
_num=1

.. będziesz mógł uzyskać do niego dostęp w b.py z symbolem nazwy modułu:

#b.py
import a
...
foo = a._num # 1

Aby zaimportować tylko nieprywatne z a.py, musisz użyć składni from :

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

Ze względu na przejrzystość lepiej jest jednak wyraźnie powiedzieć podczas importowania nazw z modułów, zamiast importować je wszystkie za pomocą „*”:

#b.py
from a import name1 
from a import name2
...
Ben Wilhelm
źródło
1
gdzie określasz, które funkcje / biblioteki są importowane? w init .py?
FistOfFury
Nie ma ryzyka kolizji nazw, gdy _namessą wywoływane import a- są to dostępy, jak a._namesprzy użyciu tego stylu.
Josiah Yoder,
@FistOfFury Tak, określasz funkcje importowane do __init__.pypliku. Zobacz tutaj o pomoc w tej sprawie.
Mike Williamson,
29

Python pozwala członkom klasy prywatnej z podwójnym prefiksem podkreślenia. Ta technika nie działa na poziomie modułu, więc myślę, że jest to błąd w Dive Into Python.

Oto przykład funkcji klasy prywatnej:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails
Andrew Hare
źródło
2
Myślę, że intencją PO jest pisanie funkcji, które nie są dostępne poza, na przykład, pakietem komercyjnym. W związku z tym odpowiedź nie jest kompletna. Funkcja __bar () jest nadal dostępna z zewnątrz poprzez f._foo__bar (). Dlatego podwójne znaki podkreślenia nie powodują, że jest on prywatny.
SevakPrime
24

Możesz dodać funkcję wewnętrzną:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

Coś takiego, jeśli naprawdę potrzebujesz takiego poziomu prywatności.

Ilian Zapryanov
źródło
9
Dlaczego nie jest to najlepsza odpowiedź?
safay
2

To starożytne pytanie, ale zmienne zniekształcone zarówno w module prywatnym (jeden podkreślnik), jak i prywatnym (dwa podkreślenia) są teraz objęte standardową dokumentacją:

Samouczek języka Python » Klasy » Zmienne prywatne

użytkownik1338062
źródło
1

osadzone z zamknięciami lub funkcjami to jeden sposób. Jest to powszechne w JS, chociaż nie jest wymagane w przypadku platform innych niż przeglądarki lub pracowników przeglądarki.

W Pythonie wydaje się to trochę dziwne, ale jeśli coś naprawdę musi być ukryte, może to być droga. Bardziej do rzeczy przy użyciu API Pythona i trzymanie rzeczy, które wymagają ukrycia w C (lub innym języku) jest prawdopodobnie najlepszym sposobem. W przeciwnym razie wybrałbym wstawienie kodu do funkcji, wywołanie go i zwrócenie elementów, które chcesz wyeksportować.

Sława Marsa
źródło
-11

Python ma trzy tryby:., Prywatny, publiczny i chroniony. Podczas importowania modułu dostępny jest tylko tryb publiczny. Takich prywatnych i chronionych modułów nie można wywoływać z zewnątrz modułu, tj. Kiedy jest importowany.

sravan
źródło