Jika ragu, biarkan "publik" - maksud saya, jangan menambahkan apa pun untuk mengaburkan nama atribut Anda. Jika Anda memiliki kelas dengan beberapa nilai internal, jangan repot-repot. Alih-alih menulis:
class Stack(object):
def __init__(self):
self.__storage = [] # Too uptight
def push(self, value):
self.__storage.append(value)
tulis ini secara default:
class Stack(object):
def __init__(self):
self.storage = [] # No mangling
def push(self, value):
self.storage.append(value)
Ini pasti cara kontroversial dalam melakukan sesuatu. Pemula Python hanya membencinya dan bahkan beberapa orang Python lama tidak menyukai default ini - tetapi ini adalah default, jadi saya sangat menyarankan Anda untuk mengikutinya, bahkan jika Anda merasa tidak nyaman.
Jika Anda benar - benar ingin mengirim pesan "Can't touch this!" kepada pengguna Anda, cara yang biasa dilakukan adalah mendahului variabel dengan satu garis bawah. Ini hanya konvensi, tetapi orang-orang memahaminya dan berhati-hati saat menangani hal-hal seperti itu:
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)
Ini juga bisa berguna untuk menghindari konflik antara nama properti dan nama atribut:
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
Bagaimana dengan garis bawah ganda? Nah, sihir garis bawah ganda digunakan terutama untuk menghindari overloading yang tidak disengaja pada metode dan konflik nama dengan atribut superclass . Ini bisa sangat berguna jika Anda menulis kelas yang diharapkan dapat diperpanjang berkali-kali.
Jika Anda ingin menggunakannya untuk tujuan lain, Anda bisa, tetapi ini tidak biasa atau tidak disarankan.
EDIT : Mengapa demikian? Nah, gaya Python yang biasa tidak menekankan pada menjadikan hal-hal pribadi - sebaliknya! Ada banyak alasan untuk itu - kebanyakan dari mereka kontroversial ... Mari kita lihat beberapa di antaranya.
Python memiliki properti
Kebanyakan bahasa OO saat ini menggunakan pendekatan yang berlawanan: apa yang tidak boleh digunakan tidak boleh terlihat, jadi atribut harus privat. Secara teoritis, ini akan menghasilkan kelas yang lebih mudah dikelola, kurang digabungkan, karena tidak ada yang akan mengubah nilai di dalam objek secara sembarangan.
Namun, tidak sesederhana itu. Misalnya, kelas Java memang memiliki banyak atribut dan getter yang hanya mendapatkan nilai dan penyetel yang baru saja menyetel nilainya. Anda perlu, katakanlah, tujuh baris kode untuk mendeklarasikan satu atribut - yang menurut programmer Python sangat rumit. Selain itu, dalam praktiknya, Anda cukup menulis seluruh kode ini untuk mendapatkan satu bidang publik, karena Anda dapat mengubah nilainya menggunakan getter dan setter.
Jadi mengapa harus mengikuti kebijakan private-by-default ini? Jadikan atribut Anda publik secara default. Tentu saja, ini menjadi masalah di Java, karena jika Anda memutuskan untuk menambahkan beberapa validasi ke atribut Anda, Anda harus mengubah semua
person.age = age;
dalam kode Anda untuk, katakanlah,
person.setAge(age);
setAge()
makhluk:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
this.age = 0;
}
}
Jadi di Java (dan bahasa lain), defaultnya adalah menggunakan getter dan setter, karena mereka dapat mengganggu untuk menulis tetapi dapat menghemat banyak waktu jika Anda berada dalam situasi yang telah saya jelaskan.
Namun, Anda tidak perlu melakukannya dengan Python, karena Python memiliki properti. Jika Anda memiliki kelas ini:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
dan kemudian Anda memutuskan untuk memvalidasi usia, Anda tidak perlu mengubah person.age = age
potongan kode Anda. Tambahkan saja properti (seperti yang ditunjukkan di bawah)
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
Jika Anda bisa melakukannya dan masih menggunakannya person.age = age
, mengapa Anda menambahkan bidang pribadi dan pengambil dan penyetel?
(Juga, lihat Python bukan Java dan artikel ini tentang kerugian menggunakan getter dan setter .).
Semuanya tetap terlihat - dan mencoba menyembunyikan hanya memperumit pekerjaan Anda
Bahkan dalam bahasa yang memiliki atribut privat, Anda dapat mengaksesnya melalui semacam pustaka refleksi / introspeksi. Dan orang banyak melakukannya, dalam kerangka kerja dan untuk menyelesaikan kebutuhan mendesak. Masalahnya adalah bahwa perpustakaan introspeksi hanyalah cara yang sulit untuk melakukan apa yang dapat Anda lakukan dengan atribut publik.
Karena Python adalah bahasa yang sangat dinamis, menambahkan beban ini ke kelas Anda akan menjadi kontraproduktif.
Masalahnya tidak mungkin untuk dilihat - itu harus dilihat
Untuk Pythonista, enkapsulasi bukanlah ketidakmampuan melihat internal kelas, tetapi kemungkinan untuk menghindari melihatnya. Yang saya maksud adalah, enkapsulasi adalah properti dari komponen yang memungkinkannya digunakan tanpa pengguna khawatir tentang detail internal. Jika Anda dapat menggunakan sebuah komponen tanpa mengganggu diri Anda sendiri tentang implementasinya, maka itu telah dienkapsulasi (menurut pendapat programmer Python).
Sekarang, jika Anda menulis kelas Anda sedemikian rupa, Anda dapat menggunakannya tanpa harus memikirkan detail implementasi, tidak ada masalah jika Anda ingin melihat ke dalam kelas karena suatu alasan. Intinya adalah: API Anda harus bagus dan sisanya adalah detail.
Guido berkata begitu
Nah, ini tidak kontroversial: dia bilang begitu, sebenarnya . (Cari "kimono terbuka".)
Ini adalah budaya
Ya, ada beberapa alasan, tetapi tidak ada alasan kritis. Ini sebagian besar merupakan aspek budaya pemrograman dengan Python. Terus terang, bisa juga sebaliknya - tapi sebenarnya tidak. Selain itu, Anda dapat dengan mudah bertanya sebaliknya: mengapa beberapa bahasa menggunakan atribut privat secara default? Untuk alasan utama yang sama seperti untuk latihan Python: karena itu adalah budaya bahasa-bahasa ini, dan setiap pilihan memiliki kelebihan dan kekurangan.
Karena sudah ada budaya ini, sebaiknya Anda mengikutinya. Jika tidak, Anda akan terganggu oleh programmer Python yang meminta Anda untuk menghapusnya __
dari kode Anda ketika Anda mengajukan pertanyaan di Stack Overflow :)
Pertama - Apa nama mangling?
Name mangling dipanggil saat Anda berada dalam definisi dan penggunaan kelas
__any_name
atau__any_name_
, yaitu, dua (atau lebih) garis bawah di depan dan paling banyak satu garis bawah.Dan sekarang:
Penggunaan yang nyata adalah untuk mencegah subclassers menggunakan atribut yang digunakan kelas.
Nilai potensial adalah dalam menghindari benturan nama dengan subclassers yang ingin mengganti perilaku, sehingga fungsionalitas kelas induk tetap berfungsi seperti yang diharapkan. Namun, contoh dalam dokumentasi Python tidak dapat diganti oleh Liskov, dan tidak ada contoh yang terlintas dalam pikiran saya yang menurut saya berguna ini.
Kekurangannya adalah ia meningkatkan beban kognitif untuk membaca dan memahami basis kode, dan terutama saat men-debug di mana Anda melihat nama garis bawah ganda di sumber dan nama yang rusak di debugger.
Pendekatan pribadi saya adalah dengan sengaja menghindarinya. Saya bekerja pada basis kode yang sangat besar. Penggunaan yang jarang dari itu menonjol seperti ibu jari yang sakit dan tampaknya tidak dibenarkan.
Anda harus menyadarinya agar Anda mengetahuinya saat melihatnya.
PEP 8
PEP 8 , panduan gaya pustaka standar Python, saat ini mengatakan (diringkas):
Bagaimana cara kerjanya?
Jika Anda menambahkan dua garis bawah (tanpa mengakhiri garis bawah ganda) dalam definisi kelas, namanya akan dihancurkan, dan garis bawah yang diikuti dengan nama kelas akan ditambahkan ke objek:
Perhatikan bahwa nama hanya akan rusak ketika definisi kelas diurai:
Juga, mereka yang baru mengenal Python terkadang mengalami kesulitan memahami apa yang terjadi ketika mereka tidak dapat mengakses nama yang mereka lihat didefinisikan dalam definisi kelas secara manual. Ini bukan alasan kuat untuk menentangnya, tetapi ini adalah sesuatu yang perlu dipertimbangkan jika Anda memiliki audiens yang belajar.
Satu Garis Bawah?
Ketika niat saya adalah agar pengguna tidak menggunakan atribut, saya cenderung hanya menggunakan satu garis bawah, tetapi itu karena dalam model mental saya, subkelas akan memiliki akses ke nama (yang selalu mereka miliki, karena mereka dapat dengan mudah melihat nama yang hancur).
Jika saya meninjau kode yang menggunakan
__
awalan, saya akan bertanya mengapa mereka memanggil nama mangling, dan jika mereka tidak dapat melakukannya dengan baik dengan satu garis bawah, perlu diingat bahwa jika subclassers memilih nama yang sama untuk kelas dan atribut kelas akan ada tabrakan nama meskipun demikian.sumber
Saya tidak akan mengatakan bahwa latihan menghasilkan kode yang lebih baik. Pengubah visibilitas hanya mengalihkan Anda dari tugas yang ada, dan sebagai efek samping memaksa antarmuka Anda untuk digunakan sesuai keinginan Anda. Secara umum, menegakkan visibilitas mencegah pemrogram mengacaukan banyak hal jika mereka belum membaca dokumentasi dengan benar.
Solusi yang jauh lebih baik adalah rute yang disarankan Python: Kelas dan variabel Anda harus didokumentasikan dengan baik, dan perilakunya jelas. Sumbernya harus tersedia. Ini adalah cara yang jauh lebih luas dan dapat diandalkan untuk menulis kode.
Strategi saya dengan Python adalah ini:
Di atas segalanya, harus jelas apa yang dilakukan semua hal. Dokumentasikan jika orang lain akan menggunakannya. Dokumentasikan jika Anda ingin berguna dalam waktu satu tahun.
Sebagai catatan tambahan, Anda harus benar-benar pergi dengan dilindungi dalam bahasa-bahasa lain itu: Anda tidak pernah tahu kelas Anda mungkin diwarisi nanti dan untuk apa kelas itu dapat digunakan. Paling baik hanya melindungi variabel-variabel yang Anda yakin tidak dapat atau tidak boleh digunakan oleh kode asing.
sumber
Anda tidak boleh memulai dengan data pribadi dan menjadikannya publik jika diperlukan. Sebaliknya, Anda harus mulai dengan mencari tahu antarmuka objek Anda. Yaitu, Anda harus mulai dengan mencari tahu apa yang dilihat dunia (hal-hal umum) dan kemudian mencari tahu hal-hal pribadi apa yang diperlukan agar hal itu terjadi.
Bahasa lain membuat sulit untuk menjadikan privat yang dulu bersifat publik. Yaitu saya akan memecahkan banyak kode jika saya membuat variabel saya pribadi atau dilindungi. Tetapi dengan properti di python ini tidak terjadi. Sebaliknya, saya dapat mempertahankan antarmuka yang sama bahkan dengan mengatur ulang data internal.
Perbedaan antara _ dan __ adalah bahwa python benar-benar berusaha untuk menerapkan yang terakhir. Tentu saja, itu tidak berusaha terlalu keras tetapi itu membuatnya sulit. Dengan _ hanya memberi tahu programmer lain apa maksudnya, mereka bebas mengabaikannya dengan risiko yang ditimbulkan. Tetapi mengabaikan aturan itu terkadang membantu. Contohnya termasuk debugging, hacks sementara, dan bekerja dengan kode pihak ketiga yang tidak dimaksudkan untuk digunakan dengan cara Anda menggunakannya.
sumber
Sudah ada banyak jawaban bagus untuk ini, tapi saya akan menawarkan yang lain. Ini juga sebagian merupakan tanggapan terhadap orang-orang yang terus mengatakan bahwa garis bawah ganda tidak bersifat pribadi (sebenarnya).
Jika Anda melihat Java / C #, keduanya memiliki private / protected / public. Semua ini adalah konstruksi waktu kompilasi . Mereka hanya diberlakukan pada saat kompilasi. Jika Anda menggunakan refleksi di Java / C #, Anda dapat dengan mudah mengakses metode privat.
Sekarang setiap kali Anda memanggil fungsi dengan Python, Anda secara inheren menggunakan refleksi. Potongan kode ini sama dengan Python.
Sintaks "titik" hanyalah gula sintaksis untuk bagian kode yang terakhir. Sebagian besar karena menggunakan getattr sudah jelek dengan hanya satu pemanggilan fungsi. Itu menjadi lebih buruk dari sana.
Jadi dengan itu, tidak mungkin ada versi Java / C # dari private, karena Python tidak mengkompilasi kodenya. Java dan C # tidak dapat memeriksa apakah suatu fungsi bersifat privat atau publik pada waktu proses, karena informasi tersebut hilang (dan tidak memiliki pengetahuan tentang dari mana fungsi tersebut dipanggil).
Sekarang dengan informasi itu, nama pengeroyokan pada garis bawah ganda paling masuk akal untuk mencapai "privasi". Sekarang ketika sebuah fungsi dipanggil dari instance 'self' dan ia memperhatikan bahwa itu dimulai dengan '__', itu hanya melakukan nama mangling di sana. Itu hanya gula sintaksis. Gula sintaksis itu memungkinkan padanan 'privat' dalam bahasa yang hanya menggunakan refleksi untuk akses anggota data.
Penafian: Saya belum pernah mendengar siapa pun dari pengembangan Python mengatakan hal seperti ini. Alasan sebenarnya dari kurangnya "privat" adalah budaya, tetapi Anda juga akan melihat bahwa kebanyakan bahasa skrip / interpretasi tidak memiliki privat. Privat yang dapat diberlakukan secara ketat tidak praktis dalam hal apa pun kecuali untuk waktu kompilasi.
sumber
Pertama: Mengapa Anda ingin menyembunyikan data Anda? Mengapa itu sangat penting?
Seringkali Anda tidak benar-benar ingin melakukannya tetapi Anda melakukannya karena orang lain melakukannya.
Jika Anda benar-benar tidak ingin orang menggunakan sesuatu, tambahkan satu garis bawah di depannya. Itu saja ... Pythonistas tahu bahwa hal-hal dengan satu garis bawah tidak dijamin akan berfungsi setiap saat dan dapat berubah tanpa Anda sadari.
Begitulah cara kami hidup dan kami setuju dengan itu.
Menggunakan dua garis bawah akan membuat kelas Anda sangat buruk untuk subkelas sehingga Anda bahkan tidak ingin bekerja seperti itu.
sumber
Jawaban yang dipilih berfungsi dengan baik dalam menjelaskan bagaimana properti menghapus kebutuhan akan atribut pribadi , tetapi saya juga akan menambahkan bahwa fungsi pada tingkat modul menghilangkan kebutuhan akan metode pribadi .
Jika Anda mengubah metode menjadi fungsi pada tingkat modul, Anda menghilangkan peluang subclass untuk menimpanya. Memindahkan beberapa fungsionalitas ke level modul lebih bersifat Pythonic daripada mencoba menyembunyikan metode dengan nama mangling.
sumber
Cuplikan kode berikut akan menjelaskan semua kasus yang berbeda:
tanpa garis bawah (a)
mencetak semua atribut yang valid dari Objek Uji
Di sini, Anda dapat melihat bahwa nama __a telah diubah menjadi _Test__a untuk mencegah variabel ini diganti oleh subclass mana pun. Konsep ini dikenal sebagai "Name Mangling" di python. Anda dapat mengakses ini seperti ini:
Demikian pula, dalam kasus _a, variabel hanya untuk memberi tahu pengembang bahwa itu harus digunakan sebagai variabel internal kelas itu, juru bahasa python tidak akan melakukan apa pun bahkan jika Anda mengaksesnya, tetapi ini bukan praktik yang baik.
variabel dapat diakses dari mana saja seperti variabel kelas publik.
Semoga jawabannya membantu Anda :)
sumber
Sekilas harus sama dengan bahasa lain (di bawah "other" maksud saya Java atau C ++), tetapi sebenarnya tidak.
Di Java, Anda menjadikan pribadi semua variabel yang seharusnya tidak dapat diakses di luar. Dalam waktu yang sama dengan Python Anda tidak dapat mencapai ini karena tidak ada "privasi" (seperti yang dikatakan salah satu prinsip Python - "Kita semua orang dewasa"). Jadi garis bawah ganda hanya berarti "Teman-teman, jangan gunakan bidang ini secara langsung". Arti yang sama memiliki singe underscore, yang pada saat yang sama tidak menimbulkan sakit kepala saat Anda harus mewarisi dari class yang dipertimbangkan (hanya contoh kemungkinan masalah yang disebabkan oleh garis bawah ganda).
Jadi, saya akan merekomendasikan Anda untuk menggunakan garis bawah tunggal secara default untuk anggota "pribadi".
sumber
"Jika ragu apakah suatu variabel harus privat atau dilindungi, lebih baik menggunakan privat." - ya, hal yang sama berlaku di Python.
Beberapa jawaban di sini mengatakan tentang 'konvensi', tetapi jangan memberikan tautan ke konvensi tersebut. Panduan otoritatif untuk Python, PEP 8 menyatakan secara eksplisit:
Perbedaan antara publik dan swasta, dan nama mangling dengan Python telah dipertimbangkan dalam jawaban lain. Dari tautan yang sama,
sumber
# CONTOH PROGRAM UNTUK mangling nama Python
Kelas Demo: __any_name = "__any_name" _ any_other_name = "_ any_other_name "
[n untuk n di dir (Demo) if 'any' in n] # MEMBERIKAN OUTPUT SEBAGAI ['_Demo__any_name', ' Demo__any_other_name ']
sumber