Menentukan fungsi modul pribadi dalam python

238

Menurut http://www.faqs.org/docs/diveintopython/fileinfo_private.html :

Seperti kebanyakan bahasa, Python memiliki konsep elemen pribadi:

  • Fungsi pribadi, yang tidak dapat dipanggil dari luar modul mereka

Namun, jika saya mendefinisikan dua file:

#a.py
__num=1

dan:

#b.py
import a
print a.__num

ketika saya menjalankannya b.pymencetak 1tanpa memberikan pengecualian. Apakah diveintopython salah, atau apakah saya salah mengerti sesuatu? Dan ada beberapa cara untuk melakukan mendefinisikan fungsi modul sebagai pribadi?

olamundo
sumber
Bukan berarti diveintopython salah, tetapi dalam contoh mereka: >>> 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 () adalah turunan dari kelas. Yang memberikan pengecualian ini ketika Anda menggunakan garis bawah ganda. Sedangkan dalam kasus Anda, Anda tidak membuat kelas, Anda baru saja membuat modul. Lihat juga: stackoverflow.com/questions/70528/…
Homero Esmeraldo

Jawaban:

323

Dalam Python, "privasi" bergantung pada tingkat persetujuan "orang dewasa" - Anda tidak dapat memaksanya (lebih dari yang Anda bisa dalam kehidupan nyata ;-). Satu garis bawah utama berarti Anda tidak seharusnya mengaksesnya "dari luar" - dua garis bawah utama (tanpa garis bawah garis) membawa pesan lebih kuat ... tetapi, pada akhirnya, masih tergantung pada sosial konvensi dan konsensus: Introspeksi Python cukup kuat sehingga Anda tidak dapat memborgol setiap programmer lain di dunia untuk menghormati keinginan Anda.

((Btw, meskipun ini adalah rahasia yang dipegang erat, hampir sama berlaku untuk C ++: dengan sebagian besar kompiler, #define private publicgaris sederhana sebelum #includeing .hfile Anda adalah semua yang diperlukan untuk pembuat kode cerdik untuk membuat hash "privasi" Anda ...! -) )

Alex Martelli
sumber
82
Catatan Anda tentang C ++ salah. Dengan menggunakan #define public private Anda mengubah kode yang akan dikirim ke kompiler, yang merupakan tempat nama mangling terjadi.
rhinoinrepose
14
Juga C ++ mangling tidak jelas, tetapi hampir tidak rahasia. Anda bisa "mengintrospeksi" biner yang diproduksi oleh C ++ juga. OT, maaf.
Prof. Falken
47
Sebagai pembaruan ke @rhinoinrepose, ini bukan saja salah, tetapi juga perilaku yang tidak terdefinisi menurut standar untuk mendefinisikan kembali kata kunci dengan makro praprosesor.
Cory Kramer
3
@AlexMartelli Tidak sepribadi yang static void foo()didapatnya. Setidaknya disembunyikan ke linker, dan fungsinya dapat dihapus seluruhnya dengan inlining.
user877329
3
Dalam kehidupan nyata orang dituntut jika mereka melanggar hukum
Lay González
289

Mungkin ada kebingungan antara privat kelas dan privat modul .

Sebuah modul pribadi dimulai dengan satu garis bawah
elemen tersebut tidak disalin bersama saat menggunakan from <module_name> import *bentuk perintah import; namun diimpor jika menggunakan import <moudule_name>sintaks ( lihat jawaban Ben Wilhelm )
Cukup hapus satu garis bawah dari nomor .__ dari contoh pertanyaan dan itu tidak akan ditampilkan dalam modul yang mengimpor a.py menggunakan from a import *sintaks.

Sebuah kelas privat dimulai dengan dua garis bawah (alias Dunder yaitu d-ouble bawah skor)
variabel tersebut memiliki nama "hancur" untuk menyertakan classname dll
Hal ini masih bisa diakses di luar logika kelas, melalui nama hancur.
Meskipun nama mangling dapat berfungsi sebagai alat pencegahan ringan terhadap akses yang tidak sah, tujuan utamanya adalah untuk mencegah kemungkinan tabrakan nama dengan anggota kelas dari kelas leluhur. Lihat referensi Alex Martelli yang lucu tapi akurat untuk memberikan persetujuan pada orang dewasa saat dia menggambarkan konvensi yang digunakan terkait dengan variabel-variabel ini.

>>> 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
sumber
Baiklah, TIL. Namun, ada alasan mengapa mereka tidak menegakkan tingkat modul __private_function? Saya mengalami ini dan mendapat kesalahan karena itu.
Santa
Terima kasih atas kata-kata baik @Terrabits, tapi saya dengan senang hati berdiri di belakang Alex (jauh di belakang!) Dalam semua hal 'Python'. Lebih jauh lagi, jawabannya biasanya lebih ringkas dan lucu sambil mempertahankan tingkat otoritas yang tinggi karena Alex memberikan kontribusi yang luas untuk bahasa dan komunitas.
mjv
1
@ mjv Ini penjelasan yang sangat membantu! Terima kasih! Saya agak bingung tentang perilaku ini untuk sementara waktu. Saya berharap pilihannya adalah untuk melemparkan beberapa jenis kesalahan selain dari AttributeError jika Anda mencoba untuk langsung mengakses kelas privat; mungkin "PrivateAccessError" atau sesuatu yang lebih eksplisit / bermanfaat. (Karena mendapatkan kesalahan yang tidak memiliki atribut tidak benar - benar benar).
HFBrowning
82

Pertanyaan ini tidak sepenuhnya dijawab, karena privasi modul tidak sepenuhnya konvensional, dan karena menggunakan impor mungkin atau mungkin tidak mengenali privasi modul, tergantung bagaimana penggunaannya.

Jika Anda mendefinisikan nama pribadi dalam modul, nama-nama itu akan diimpor ke skrip yang menggunakan sintaks, 'import module_name'. Jadi, anggap Anda telah mendefinisikan dengan benar dalam contoh Anda modul privat, _num, dalam a.py, seperti itu ..

#a.py
_num=1

..you akan dapat mengaksesnya di b.py dengan simbol nama modul:

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

Untuk hanya mengimpor non-privat dari a.py, Anda harus menggunakan sintaks dari :

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

Namun, demi kejelasan, sebaiknya eksplisit ketika mengimpor nama dari modul, daripada mengimpor semuanya dengan '*':

#b.py
from a import name1 
from a import name2
...
Ben Wilhelm
sumber
1
di mana Anda menentukan fungsi / perpustakaan mana yang diimpor? di init .py?
FistOfFury
Tidak ada risiko tabrakan nama ketika _namesdipanggil dengan import a- mereka diakses seperti a._namesketika menggunakan gaya ini.
Josiah Yoder
@FistOfFury Ya, Anda menentukan fungsi yang diimpor dalam __init__.pyfile. Lihat di sini untuk bantuan tentang itu.
Mike Williamson
29

Python memungkinkan untuk kelas privat anggota dengan awalan garis bawah ganda. Teknik ini tidak berfungsi pada level modul jadi saya pikir ini adalah kesalahan dalam Dive Into Python.

Berikut adalah contoh fungsi kelas privat:

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

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails
Andrew Hare
sumber
2
Saya pikir maksud OP adalah untuk menulis fungsi yang tidak dapat diakses di luar, misalnya, paket komersial. Sehubungan dengan itu, jawaban ini tidak lengkap. Fungsi __bar () masih dapat diakses dari luar melalui f._foo__bar (). Oleh karena itu, garis bawah dua sisi tidak menjadikannya pribadi.
SevakPrime
24

Anda dapat menambahkan fungsi bagian dalam:

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

Sesuatu seperti itu jika Anda benar-benar membutuhkan tingkat privasi itu.

Ilian Zapryanov
sumber
9
Mengapa ini bukan jawaban terbaik?
safay
2

Ini adalah pertanyaan kuno, tetapi kedua variabel modul privat (satu garis bawah) dan kelas-privat (dua garis bawah) sekarang tercakup dalam dokumentasi standar:

Tutorial Python » Kelas » Variabel Pribadi

pengguna1338062
sumber
1

tertanam dengan penutupan atau fungsi adalah satu cara. Ini biasa terjadi di JS meskipun tidak diperlukan untuk platform atau browser non-browser.

Dalam Python sepertinya agak aneh, tetapi jika sesuatu benar-benar perlu disembunyikan daripada itu mungkin jalan. Lebih tepatnya menggunakan python API dan menjaga hal-hal yang perlu disembunyikan di C (atau bahasa lain) mungkin merupakan cara terbaik. Gagal bahwa saya akan pergi untuk menempatkan kode di dalam fungsi, memanggil itu dan mengembalikannya barang yang ingin Anda ekspor.

Ketenaran Mars
sumber
-11

Python memiliki tiga mode via., Private, public, dan protected. Sementara mengimpor modul, hanya mode publik yang dapat diakses.

sravan
sumber