Kelas "Private" (implementasi) dengan Python

107

Saya mengkodekan modul Python kecil yang terdiri dari dua bagian:

  • beberapa fungsi yang mendefinisikan antarmuka publik,
  • kelas implementasi yang digunakan oleh fungsi-fungsi di atas, tetapi tidak berarti di luar modul.

Pada awalnya, saya memutuskan untuk "menyembunyikan" kelas implementasi ini dengan mendefinisikannya di dalam fungsi yang menggunakannya, tetapi ini menghambat keterbacaan dan tidak dapat digunakan jika beberapa fungsi menggunakan kembali kelas yang sama.

Jadi, selain komentar dan dokumen, apakah ada mekanisme untuk menandai kelas sebagai "pribadi" atau "internal"? Saya mengetahui mekanisme garis bawah, tetapi seperti yang saya pahami, ini hanya berlaku untuk variabel, fungsi, dan nama metode.

oparisy
sumber

Jawaban:

175

Gunakan satu awalan garis bawah:

class _Internal:
    ...

Ini adalah konvensi resmi Python untuk simbol 'internal'; "from module import *" tidak mengimpor objek dengan awalan garis bawah.

Edit: Referensi ke konvensi garis bawah tunggal

Ferdinand Beyer
sumber
3
Saya tidak tahu aturan garis bawah diperluas ke kelas. Saya tidak ingin mengacaukan namespace saya saat mengimpor, jadi perilaku inilah yang saya cari. Terima kasih!
oparisy
1
Karena Anda menyatakan bahwa ini adalah konvensi python "resmi", alangkah baiknya dengan tautan. Posting lain di sini mengatakan bahwa semua adalah cara resmi dan memang menautkan ke dokumentasi.
flodin
11
python.org/dev/peps/pep-0008 - _single_leading_underscore: indikator "penggunaan internal" lemah. Misalnya "dari M import *" tidak mengimpor objek yang namanya dimulai dengan garis bawah. - Kelas untuk penggunaan internal memiliki garis bawah di depan
Miles
2
Garis bawah utama adalah konvensi untuk menandai sesuatu sebagai internal, "jangan main-main dengan ini", sedangkan semuanya lebih untuk modul yang dirancang untuk digunakan dengan "dari M import *", tanpa selalu menyiratkan bahwa pengguna modul tidak boleh menyentuh kelas itu.
Miles
65

Pendeknya:

  1. Anda tidak dapat memaksakan privasi . Tidak ada kelas / metode / fungsi privat di Python. Setidaknya, privasi tidak ketat seperti di bahasa lain, seperti Java.

  2. Anda hanya dapat menunjukkan / menyarankan privasi . Ini mengikuti konvensi. Konvensi python untuk menandai kelas / fungsi / metode sebagai privat adalah untuk mengawali dengan _ (garis bawah). Misalnya, def _myfunc()atau class _MyClass:. Anda juga dapat membuat pseudo-privacy dengan mengawali metode dengan dua garis bawah (misalnya:) __foo. Anda tidak dapat mengakses metode secara langsung, tetapi Anda masih dapat memanggilnya melalui awalan khusus menggunakan nama kelas (misalnya:) _classname__foo. Jadi yang terbaik yang dapat Anda lakukan adalah menunjukkan / menyarankan privasi, bukan menegakkannya.

Python seperti perl dalam hal ini. Untuk memparafrasekan baris terkenal tentang privasi dari buku Perl, filosofinya adalah Anda harus tetap berada di luar ruang tamu karena Anda tidak diundang, bukan karena dilindungi dengan senapan.

Untuk informasi lebih lanjut:

Karl Fast
sumber
Menanggapi # 1, Anda semacam dapat menerapkan metode privasi. Menggunakan garis bawah ganda seperti __method (self) akan membuatnya tidak dapat diakses di luar kelas. Tetapi ada cara lain dengan menyebutnya seperti, Foo () ._ Foo__method (). Saya kira itu hanya mengganti namanya menjadi sesuatu yang lebih esoterik.
Evan Fosmark
1
Kutipan itu akurat.
Paul Draper
37

Tentukan __all__, daftar nama yang ingin Anda ekspor ( lihat dokumentasi ).

__all__ = ['public_class'] # don't add here the 'implementation_class'
UncleZeiv
sumber
10

Pola yang terkadang saya gunakan adalah ini:

Tentukan kelas:

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

Buat instance kelas, yang menimpa nama kelas:

x = x()

Tentukan simbol yang mengekspos fungsionalitas:

doThis = x.doThis
doThat = x.doThat

Hapus instance itu sendiri:

del x

Sekarang Anda memiliki modul yang hanya mengekspos fungsi publik Anda.

theller
sumber
2
Perlu waktu sebentar untuk memahami tujuan / fungsi dari "menimpa nama kelas", tetapi saya tersenyum lebar ketika melakukannya. Tidak yakin kapan saya akan menggunakannya. :)
Zach Young
Apakah ada nama untuk teknik ini?
Bishwas Mishra
8

Konvensi ini menambahkan "_" ke kelas, fungsi, dan variabel internal.

Benjamin Peterson
sumber
4

Untuk mengatasi masalah konvensi desain, dan seperti yang dikatakan Christopher, sebenarnya tidak ada yang namanya "pribadi" dalam Python. Ini mungkin terdengar aneh bagi seseorang yang berasal dari latar belakang C / C ++ (seperti saya beberapa waktu lalu), tetapi pada akhirnya, Anda mungkin akan menyadari bahwa mengikuti konvensi sudah cukup.

Melihat sesuatu yang memiliki garis bawah di depan seharusnya menjadi petunjuk yang cukup baik untuk tidak menggunakannya secara langsung. Jika Anda khawatir dengan help(MyClass)keluaran yang berantakan (yang dilihat semua orang saat mencari tentang cara menggunakan kelas), atribut / kelas yang digarisbawahi tidak disertakan di sana, jadi Anda hanya akan mendapatkan penjelasan antarmuka "publik".

Selain itu, memiliki semua yang bersifat publik memiliki keistimewaannya sendiri yang luar biasa, seperti misalnya, Anda dapat menguji hampir semua hal dari luar (yang tidak dapat Anda lakukan dengan konstruksi pribadi C / C ++).

Dimitri Tcaciuc
sumber
4

Gunakan dua garis bawah untuk mengawali nama pengenal "pribadi". Untuk kelas dalam modul, gunakan satu garis bawah dan mereka tidak akan diimpor menggunakan "from module import *".

class _MyInternalClass:
    def __my_private_method:
        pass

(Tidak ada yang namanya benar "pribadi" dalam Python. Sebagai contoh, Python hanya secara otomatis merusak nama anggota kelas dengan garis bawah ganda menjadi __clssname_mymember. Jadi sungguh, jika Anda tahu nama yang rusak Anda dapat menggunakan entitas "pribadi". . Lihat di sini. Dan tentu saja Anda dapat memilih untuk secara manual impor "internal" kelas jika Anda ingin).

chroder
sumber
Mengapa dua _? Satu sudah cukup.
S. Lott
Satu garis bawah sudah cukup untuk mencegah "impor" dari mengimpor kelas dan fungsi. Anda memerlukan dua garis bawah untuk menginduksi fitur mangling nama Python. Seharusnya lebih jelas; Saya mengedit.
chroder
Sekarang, tindak lanjutnya. Mengapa menamai mangling? Apa manfaatnya?
S. Lott
5
Manfaat yang mungkin adalah mengganggu pengembang lain yang membutuhkan akses ke variabel tersebut :)
Richard Levasseur