Apakah Python memiliki variabel "pribadi" di kelas?

578

Saya datang dari dunia Jawa dan membaca Pola, Resep, dan Idiom Bruce Eckels ' Python 3 .

Saat membaca tentang kelas, selanjutnya dikatakan bahwa dengan Python tidak perlu mendeklarasikan variabel instan. Anda hanya menggunakannya di konstruktor, dan boom, mereka ada di sana.

Jadi misalnya:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Jika itu benar, maka objek kelas apa Simplesaja dapat mengubah nilai variabel di sluar kelas.

Sebagai contoh:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

Di Jawa, kami telah diajarkan tentang variabel publik / pribadi / yang dilindungi. Kata kunci tersebut masuk akal karena kadang-kadang Anda ingin variabel dalam suatu kelas yang tidak dapat diakses oleh siapa pun di luar kelas.

Mengapa itu tidak diperlukan dalam Python?

Di mana-mana
sumber
17
Anda berarti variabel instan , bukan variabel kelas , bukan?
PaulMcG
13
Anda harus memeriksa properti: docs.python.org/library/functions.html#property . Cukup gunakan pengambil dan variabel Anda akan dilindungi.
rubik
Jawaban singkat dan jernih ada di sini . Saya harap ini akan membantu.
Premkumar chalmeti

Jawaban:

962

Itu budaya. Dengan Python, Anda tidak menulis ke instance kelas atau variabel kelas lainnya. Di Jawa, tidak ada yang mencegah Anda melakukan hal yang sama jika Anda benar - benar ingin - lagipula, Anda selalu dapat mengedit sumber kelas itu sendiri untuk mencapai efek yang sama. Python menjatuhkan kepura-puraan keamanan dan mendorong programmer untuk bertanggung jawab. Dalam praktiknya, ini bekerja dengan sangat baik.

Jika Anda ingin meniru variabel pribadi karena suatu alasan, Anda selalu dapat menggunakan __awalan dari PEP 8 . Python membuat nama-nama variabel seperti __foosehingga mereka tidak mudah terlihat dengan kode di luar kelas yang berisi mereka (meskipun Anda bisa mengelilinginya jika Anda cukup bertekad, sama seperti Anda bisa berkeliling perlindungan Jawa jika Anda bekerja di dalamnya ).

Dengan konvensi yang sama, _awalan berarti menjauh meskipun Anda secara teknis tidak dicegah melakukannya . Anda tidak bermain-main dengan variabel kelas lain yang terlihat seperti __fooatau _bar.

Kirk Strauser
sumber
14
Itu masuk akal. Namun, saya tidak berpikir ada cara apa pun di java untuk mengakses variabel pribadi di luar kelas (kecuali sebenarnya mengubah sumber kelas ofcourse). Disana?
Omnipresent
168
Saya cenderung lebih suka cara python, tapi saya pikir cara java tidak ada gunanya seperti yang Anda lakukan. Mendeklarasikan sesuatu yang pribadi dengan cepat memberi tahu seseorang yang membaca kode sesuatu yang sangat berguna: bidang ini hanya pernah dimodifikasi di dalam kelas ini.
Ned
67
@ Hadir, Anda dapat menggunakan refleksi.
rapadura
77
Biarkan saya meluruskan hal ini, jadi Python tidak menerapkan atribut publik atau pribadi karena "ini merupakan alasan keamanan dan mendorong programmer untuk bertanggung jawab", namun komunitas mendorong penggunaan "_" untuk menunjukkan variabel dan metode pribadi? Mungkin python harus secara definitif memiliki publik dan swasta tidak? Tujuan utamanya adalah untuk memberi tahu Anda API apa yang harus Anda gunakan untuk berinteraksi dengan kelas. Mereka berfungsi sebagai dokumentasi yang memberitahu Anda untuk menggunakan metode ini dan tidak menggunakannya. Mereka bukan "kepura-puraan keamanan", itu adalah dokumentasi API, yang bahkan dapat digunakan oleh IDE untuk memandu Anda!
PedroD
19
Ini adalah jawaban yang bagus dan alasan Anda tentu valid, tetapi saya tidak setuju dengan satu aspek. Tujuan pengubah akses tidak pernah untuk keamanan . Sebaliknya, mereka adalah cara untuk secara eksplisit membatasi (dan sebagian besar, menegakkan) bagian mana dari suatu kelas yang dianggap internal dan yang diekspos kepada pengguna luar kelas itu. Konvensi (budaya) tentu saja merupakan alternatif yang valid untuk mengakses pengubah, dan kedua metode memiliki pro dan kontra, tetapi menyesatkan untuk tujuan bahwa pengubah akses tingkat bahasa dimaksudkan dengan cara apa pun untuk menjadi "aman" dalam arti yang biasa. kata.
devios1
159

Variabel pribadi dalam python lebih atau kurang adalah peretasan: penafsir sengaja mengganti nama variabel.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Sekarang, jika Anda mencoba mengakses di __varluar definisi kelas, itu akan gagal:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Tetapi Anda dapat dengan mudah lolos dengan ini:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Anda mungkin tahu bahwa metode dalam OOP dipanggil seperti ini:, x.printVar() => A.printVar(x)jika A.printVar()dapat mengakses beberapa bidang dalam x, bidang ini juga dapat diakses di luar A.printVar() ... setelah semua, fungsi dibuat untuk dapat digunakan kembali, tidak ada kekuatan khusus yang diberikan untuk pernyataan di dalam.

Permainan berbeda ketika ada kompiler yang terlibat ( privasi adalah konsep level kompiler ). Ia tahu tentang definisi kelas dengan pengubah kontrol akses sehingga bisa kesalahan jika aturan tidak diikuti pada waktu kompilasi

watashiSHUN
sumber
3
singkatnya, ini bukan enkapsulasi
watashiSHUN
2
Saya bertanya-tanya apakah PHP memiliki sesuatu yang mirip dengan variabel privasinya yang konyol - karena variabel pribadi tidak benar-benar masuk akal dalam bahasa yang ditafsirkan - maksud saya optimasi apa yang dapat dilakukannya dengan mengetahui x variabel bersifat pribadi, jika tidak dikompilasi?
NoBugs
1
Bagaimana kita bisa mengacak pola variabel privat?
crisron
@crisron pertanyaan yang sama
IanS
5
@watashiSHUN "singkatnya, ini bukan enkapsulasi" => ya itu. Enkapsulasi adalah tentang hanya menggunakan API publik sehingga kode klien dilindungi dari perubahan implementasi. Konvensi penamaan adalah cara yang benar-benar valid untuk mengetahui apa itu API dan apa implementasinya, dan intinya adalah ia berfungsi.
bruno desthuilliers
30

Seperti yang disebutkan dengan benar oleh banyak komentar di atas, jangan lupa tujuan utama Access Modifiers: Untuk membantu pengguna kode memahami apa yang seharusnya diubah dan apa yang seharusnya tidak. Ketika Anda melihat bidang pribadi Anda tidak main-main dengan itu. Jadi sebagian besar gula sintaksis yang mudah dicapai dalam Python oleh _ dan __.

Ardavan
sumber
4
Saya pikir ini sama pentingnya dengan poin apa pun. Ketika kode debug (saya tahu, saya lemah untuk memperkenalkan bug), tahu kelas mana yang dapat mengubah variabel anggota menyederhanakan proses debug. Setidaknya, jika variabel dilindungi oleh beberapa ruang lingkup. Konsep serupa adalah fungsi const di C ++. Saya tahu bahwa variabel anggota tidak berubah di sana dan jadi saya bahkan tidak melihat metode itu sebagai potensi penyebab pengaturan variabel yang buruk. Meskipun dapat membuat pengembangan selanjutnya ekstensi kelas / menambahkan fitur, membatasi visibilitas kode membuatnya lebih mudah untuk di-debug.
MrMas
18

Ada variasi variabel pribadi dalam konvensi garis bawah.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

Ada beberapa perbedaan halus, tetapi demi pola pemrograman kemurnian ideologis, itu cukup baik.

Ada contoh di luar sana dari dekorator pribadi @ yang lebih dekat menerapkan konsep, tetapi YMMV. Bisa dibilang orang juga bisa menulis definisi kelas yang menggunakan meta

Shayne
sumber
14
Saya menyadari ini sangat terlambat untuk pesta tetapi tautan ini muncul di google saat googling masalah. Ini tidak menceritakan keseluruhan cerita. __xsebagai variabel di dalam kelas Asebenarnya ditulis ulang oleh kompiler _A__x, itu masih belum sepenuhnya pribadi dan masih dapat diakses.
Zorf
1
Tentu saja, jika saya melihat variabel bernama _A__x, saya tidak akan menyentuhnya. Itu bisa menular. Saya akan lari dari itu.
Mateen Ulhaq
Rasanya tidak benar - benar pribadi. Tetapi alasan prinsip untuk privat keras dalam C ++ dan Java (dll), optimisasi kompiler, tidak benar-benar ada di Python, jadi konvensi pribadi cukup baik. Konvensi Python pada umumnya adalah percaya bahwa Anda akan berperilaku tanpa pengawasan. (Dan ini adalah perangkap pemula, tetapi Anda tahu, hanya berpikir tentang desain kelas dan konsumsi)
Shayne
14

"Di java, kita telah diajarkan tentang variabel publik / pribadi / yang dilindungi"

"Kenapa itu tidak diperlukan dengan python?"

Untuk alasan yang sama, itu tidak diperlukan di Jawa.

Anda bebas menggunakan - atau tidak menggunakan privatedan protected.

Sebagai programmer Python dan Java, saya telah menemukan itu privatedan protectedsangat, konsep desain yang sangat penting. Tetapi sebagai hal praktis, dalam puluhan ribu baris Java dan Python, saya tidak pernah benar - benar menggunakan privateatau protected.

Kenapa tidak?

Inilah pertanyaan saya "dilindungi dari siapa?"

Pemrogram lain di tim saya? Mereka punya sumbernya. Apa artinya dilindungi ketika mereka dapat mengubahnya?

Pemrogram lain di tim lain? Mereka bekerja untuk perusahaan yang sama. Mereka dapat - dengan panggilan telepon - mendapatkan sumbernya.

Klien? Ini program kerja-untuk-merekrut (umumnya). Klien (umumnya) memiliki kodenya.

Jadi, siapa - tepatnya - di mana saya melindunginya?

S.Lott
sumber
119
-1: Saya setuju dengan Porculus. Ini bukan tentang melarang akses atau menyembunyikan sesuatu, ini tentang dokumentasi API implisit . Pengembang serta kompiler / juru bahasa / pemeriksa kode dengan mudah melihat anggota mana yang direkomendasikan untuk digunakan dan mana yang tidak boleh disentuh (atau setidaknya dengan hati-hati). Dalam kebanyakan kasus itu akan menjadi kekacauan yang mengerikan jika semua anggota kelas atau modul adalah publik. Pertimbangkan perbedaan anggota pribadi / terlindungi / publik sebagai layanan, dengan mengatakan: "Hei, anggota ini penting sementara mereka digunakan secara internal dan mungkin tidak berguna bagi Anda."
Oben Sonne
7
@ S.Lott: Saya setuju bahwa dokumen API memiliki prioritas lebih tinggi dan seringkali merupakan satu-satunya cara untuk mengomunikasikan penggunaan API yang dimaksudkan. Tetapi kadang-kadang nama dan visibilitas anggota (dalam hal pribadi / publik) cukup berbicara sendiri. Juga, saya melihat maksud Anda bahwa ide dokumentasi implisit tidak bekerja dengan baik di editor tanpa inspeksi API tetapi sangat membantu dalam IDE dengan penyelesaian kode. Dengan asumsi Anda telah membaca dokumen API beberapa waktu yang lalu, ini membantu Anda mengingat cara menggunakan kelas. Hal-hal tidak akan bekerja sepintar itu jika tidak ada perbedaan antara anggota pribadi dan publik.
Oben Sonne
21
Terlambat dalam diskusi, tetapi semua yang diminta Porculus dan Oben di sini ditangani dengan cukup baik oleh konvensi "awalan dengan garis bawah" (dan tanpa membahayakan yang dapat ditimbulkan oleh penyusun kompilasi dari konvensi itu)
ncoghlan
38
@ S.Lott Aku bukan pria python, jadi aku tidak akan berkomentar dari perspektif itu. Namun sebagai pengembang java ini adalah saran yang benar-benar mengerikan. -1
dbyrne
11
Wow. Anda benar-benar kehilangan poin, Anda memberikan nasihat yang sangat buruk, Anda menghina siapa pun yang tidak setuju dengan Anda tentang hal ini, tetapi Anda masih mendapatkan lencana dan lebih dari 1000 poin reputasi untuk "jawaban" ini.
Eric Duminil
12

Seperti yang disebutkan sebelumnya, Anda dapat menunjukkan bahwa variabel atau metode bersifat pribadi dengan awalan dengan garis bawah. Jika Anda merasa ini tidak cukup, Anda selalu dapat menggunakan propertydekorator. Ini sebuah contoh:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

Dengan cara ini, seseorang atau sesuatu yang direferensikan barsebenarnya mereferensikan nilai pengembalian barfungsi daripada variabel itu sendiri, dan oleh karena itu dapat diakses tetapi tidak diubah. Namun, jika seseorang benar-benar menginginkannya, mereka bisa menggunakan _bardan memberikan nilai baru padanya. Tidak ada cara pasti untuk mencegah seseorang mengakses variabel dan metode yang ingin Anda sembunyikan, seperti yang telah dikatakan berulang kali. Namun, menggunakan propertyadalah pesan yang paling jelas yang dapat Anda kirim bahwa suatu variabel tidak dapat diedit. propertyjuga dapat digunakan untuk jalur akses pengambil / penyetel / deleter yang lebih kompleks, seperti yang dijelaskan di sini: https://docs.python.org/3/library/functions.html#property

Isaac Saffold
sumber
Django juga menghargai ini.
babygame0ver
10

Python memiliki dukungan terbatas untuk pengidentifikasi pribadi, melalui fitur yang secara otomatis menambahkan nama kelas ke pengidentifikasi apa pun yang dimulai dengan dua garis bawah. Ini transparan untuk programmer, sebagian besar, tetapi efek bersihnya adalah bahwa setiap variabel bernama cara ini dapat digunakan sebagai variabel pribadi.

Lihat di sini untuk lebih lanjut tentang itu.

Secara umum, implementasi orientasi objek Python sedikit primitif dibandingkan dengan bahasa lain. Tapi sebenarnya saya menikmati ini. Ini adalah implementasi yang sangat sederhana secara konseptual dan sangat cocok dengan gaya bahasa yang dinamis.

Dan Olson
sumber
Ya. Keindahannya adalah, kemampuan metaprogramming python berarti Anda benar-benar dapat mengimplementasikan hal-hal mewah jika Anda mau (Dan ada perpustakaan yang mengimplementasikan dekorator dan hal-hal lain seperti @ private / @ protected / etc. Neraka saya bahkan melihat perpustakaan yang menambahkan kelas prototipe gaya JS tanpa alasan waras), tetapi dalam prakteknya itu tidak perlu .. Saya agak benci meme "python / js / apapun itu" karena hampir tidak pernah benar, tetapi python tidak berbagi metaprogramming chops yang dikombinasikan dengan sintaksis sederhana 'dengan bahasa itu
Shayne
8

Satu-satunya waktu saya menggunakan variabel pribadi adalah ketika saya perlu melakukan hal-hal lain ketika menulis ke atau membaca dari variabel dan karena itu saya harus memaksa penggunaan setter dan / atau pengambil.

Sekali lagi ini berlaku untuk budaya, seperti yang telah disebutkan. Saya telah mengerjakan proyek-proyek di mana membaca dan menulis variabel kelas lain adalah gratis untuk semua. Ketika satu implementasi menjadi usang butuh waktu lebih lama untuk mengidentifikasi semua jalur kode yang menggunakan fungsi itu. Ketika penggunaan setter dan getter terpaksa, pernyataan debug dapat dengan mudah ditulis untuk mengidentifikasi bahwa metode yang sudah usang telah dipanggil dan jalur kode yang menyebutnya.

Ketika Anda berada di proyek di mana siapa pun dapat menulis ekstensi, memberi tahu pengguna tentang metode usang yang akan menghilang dalam beberapa rilis maka sangat penting untuk menjaga kerusakan modul seminimal mungkin pada saat upgrade.

Jadi jawaban saya adalah; jika Anda dan kolega Anda mempertahankan satu set kode sederhana maka melindungi variabel kelas tidak selalu diperlukan. Jika Anda menulis sistem yang dapat diperluas maka menjadi penting ketika perubahan pada inti dibuat yang perlu ditangkap oleh semua ekstensi menggunakan kode.

BlueEagle
sumber
8

Maaf guys untuk "menghidupkan kembali" utas, tapi, saya harap ini akan membantu seseorang:

Di Python3 jika Anda hanya ingin "merangkum" atribut kelas, seperti di Jawa, Anda bisa melakukan hal yang sama seperti ini:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Untuk instantiate ini lakukan:

ss = Simple("lol")
ss.show()

Perhatikan bahwa: print(ss.__s)akan menimbulkan kesalahan.

Dalam praktiknya, Python3 akan mengaburkan nama atribut global. Mengubah ini seperti atribut "pribadi", seperti di Jawa. Nama atribut masih bersifat global, tetapi dengan cara yang tidak dapat diakses, seperti atribut pribadi dalam bahasa lain.

Tapi jangan takut. Itu tidak masalah. Itu melakukan pekerjaan juga. ;)

Ferrarezi
sumber
2
ini sudah ada sejak Python 1.5.2 IIRC, dan itu masih tidak mencegah mengakses atribut melalui namanya yang hancur.
bruno desthuilliers
7

konsep pribadi dan dilindungi sangat penting. Tapi python - hanya alat untuk membuat prototipe dan pengembangan cepat dengan sumber daya terbatas yang tersedia untuk pengembangan, itu sebabnya beberapa tingkat perlindungan tidak begitu ketat diikuti oleh python. Anda dapat menggunakan "__" di anggota kelas, itu berfungsi dengan baik, tetapi terlihat tidak cukup baik - setiap akses ke bidang tersebut berisi karakter ini.

Anda juga dapat memperhatikan bahwa konsep python OOP tidak sempurna, smaltalk atau ruby ​​lebih dekat dengan konsep OOP murni. Bahkan C # atau Java lebih dekat.

Python adalah alat yang sangat bagus. Tetapi ini adalah bahasa OOP yang disederhanakan. Secara sintaksis dan konseptual disederhanakan. Tujuan utama dari keberadaan python adalah untuk membawa kepada pengembang kemungkinan untuk menulis kode yang mudah dibaca dengan tingkat abstraksi yang tinggi dengan cara yang sangat cepat.

pengguna711294
sumber
Rearson Private dan Protected penting adalah bahwa dalam bahasa yang dikompilasi secara statis, kompiler dapat membuat panggilan langsung ke metode pribadi, tetapi harus bergantung pada tabel pencarian untuk metode publik. Ini bukan masalah dengan bahasa dinamis. Akhirnya bahasa seperti C ++ ada implikasi untuk pewarisan dan resolusi metode. Python dan Ruby memiliki implementasi OO yang sangat mirip, sehingga perbandingannya tidak ada artinya. Smalltalk sebenarnya tidak memiliki gagasan tentang pesan publik / pribadi. Anda bebas menambahkan pribadi sebagai kategori, tetapi ini murni saran.
Shayne
Untuk lebih jauh penegasan saya. Dari sudut pandang kebersihan codiing, ya mereka penting untuk enkapsulasi, tetapi tidak diperlukan untuk itu, sehingga dekorator @private (dll) lebih penasehat daripada apa pun, tetapi karena pribadi / publik tidak menambahkan hal yang berguna untuk pengoptimalan dalam bahasa non-statis, ini tidak diimplementasikan pada level yang dalam seperti pada bahasa yang dikompilasi seperti java atau c
Shayne
4

Python tidak memiliki variabel pribadi seperti C ++ atau Java. Anda dapat mengakses variabel anggota mana saja kapan saja jika diinginkan. Namun, Anda tidak memerlukan variabel pribadi dalam Python, karena dalam Python tidak buruk untuk mengekspos variabel anggota kelas Anda. Jika Anda perlu merangkum variabel anggota, Anda dapat melakukan ini dengan menggunakan "@ properti" nanti tanpa melanggar kode klien yang ada.

Dalam python, garis bawah tunggal "_" digunakan untuk menunjukkan, bahwa suatu metode atau variabel tidak dianggap sebagai bagian dari api publik suatu kelas dan bahwa bagian api ini dapat berubah di antara versi yang berbeda. Anda dapat menggunakan metode / variabel ini, tetapi kode Anda dapat rusak, jika Anda menggunakan versi yang lebih baru dari kelas ini.

Garis ganda berganda "__" tidak berarti "variabel pribadi". Anda menggunakannya untuk mendefinisikan variabel yang merupakan "kelas lokal" dan yang tidak dapat dengan mudah dikuasai oleh subkelas. Ini mangles nama variabel.

Sebagai contoh:

class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

self .__ nama foobar secara otomatis di-mangled ke self._A__foobar di kelas A. Di kelas B itu di-mangled ke self._B__foobar. Jadi setiap subclass dapat mendefinisikan variabel sendiri __foobar tanpa mengesampingkan variabel induknya. Tapi tidak ada yang mencegah Anda mengakses variabel yang dimulai dengan garis bawah ganda. Namun, pengubahan nama mencegah Anda memanggil variabel / metode ini secara tidak sengaja.

Saya sangat merekomendasikan untuk menonton Raymond Hettingers berbicara "Toolkit pengembangan kelas Python" dari Pycon 2013 (harus tersedia di Youtube), yang memberikan contoh yang baik mengapa dan bagaimana Anda harus menggunakan @property dan "__" - variabel instan.

Hatatister
sumber
Saya akan memeriksa pembicaraan itu. Apakah @propertyhal itu bagian dari Python standar, atau khusus untuk IDE?
bballdave025
Itu bagian dari standar sejak python 2.6. Jika Anda harus menggunakan versi yang lebih lama, masih ada kemungkinan untuk menggunakan propertyfungsi builtin, yang tersedia sejak python 2.2
Hatatister
0

Sebenarnya Anda dapat mensimulasikan C#pengambil dan penyetel menggunakan trik sederhana ini:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

Kemudian gunakan yang serupa seperti di C#:

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

Itu hanya mendeklarasikan variabel lokal statis dalam suatu fungsi yang akan memainkan peran get / set, karena itulah satu-satunya cara untuk membagikan variabel melalui metode get and set, tanpa membuatnya global untuk kelas atau file.

Ilian Zapryanov
sumber