Apa arti dari garis bawah tunggal dan ganda sebelum nama objek?

1292

Dapatkah seseorang tolong jelaskan arti sebenarnya dari memiliki garis bawah di depan nama objek dengan Python, dan perbedaan antara keduanya?

Juga, apakah makna itu tetap sama apakah objek yang dimaksud adalah variabel, fungsi, metode, dll.?

ivanleoncz
sumber
2
Sebuah jawaban pendek yang bagus dari utas lain: stackoverflow.com/a/8689983/911945
Anton Tarasenko

Jawaban:

1156

Garis Bawah Tunggal

Nama-nama, di kelas, dengan garis bawah utama hanya untuk menunjukkan kepada programmer lain bahwa atribut atau metode dimaksudkan untuk bersifat pribadi. Namun, tidak ada yang istimewa dilakukan dengan nama itu sendiri.

Mengutip PEP-8 :

_single_leading_underscore: indikator "penggunaan internal" lemah. Misalnya from M import *tidak mengimpor objek yang namanya dimulai dengan garis bawah.

Garis Bawah Ganda (Name Mangling)

Dari dokumen Python :

Setiap pengidentifikasi formulir __spam(setidaknya dua garis bawah utama, paling banyak satu garis bawah garis bawah) diganti secara teks _classname__spam, di mana classnamenama kelas saat ini dengan garis bawah utama bergaris-garis. Mangling ini dilakukan tanpa memperhatikan posisi sintaksis pengidentifikasi, sehingga dapat digunakan untuk mendefinisikan instance kelas-privat dan variabel kelas, metode, variabel yang disimpan dalam global, dan bahkan variabel yang disimpan dalam instance. pribadi ke kelas ini pada instance dari kelas lain.

Dan peringatan dari halaman yang sama:

Name mangling dimaksudkan untuk memberi kelas cara yang mudah untuk mendefinisikan variabel instance dan metode "private", tanpa harus khawatir tentang variabel instan yang ditentukan oleh kelas turunan, atau mengaitkan variabel instan dengan kode di luar kelas. Perhatikan bahwa aturan mangling sebagian besar dirancang untuk menghindari kecelakaan; masih mungkin bagi jiwa yang ditentukan untuk mengakses atau memodifikasi variabel yang dianggap pribadi.

Contoh

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
Andrew Keeton
sumber
17
Bagaimana jika ada nama variabel yang dideklarasikan dengan 2 garis bawah yang tidak ada di kelas? Itu hanya variabel normal, kan?
Dhruv Ramani
5
apa arti dari __menggandakan garis bawah sebagai nama variabel? sepertia, __ = foo()
AJ
104
Jawaban ini sangat menyesatkan, karena membuat pembaca percaya bahwa dunderscore digunakan untuk membuat atribut instan "superprivate". Ini bukan masalahnya, seperti dijelaskan di sini oleh Raymond Hettinger, yang secara eksplisit menyatakan bahwa dunderscore digunakan secara tidak benar untuk menandai anggota pribadi, sementara itu dirancang untuk menjadi kebalikan dari pribadi.
Markus Meskanen
13
@MarkusMeskanen Saya tidak setuju, jawabannya secara eksplisit menyatakan penggunaan dunderscore untuk membuat instance variabel dan metode kelas-pribadi. Sementara dunderscore dirancang untuk membuat metode dan variabel ini dengan mudah ditimpa oleh subclass (menjadikannya publik), penggunaan dunderscore mempertahankan instance pribadi untuk digunakan dalam kelas itu.
arewm
6
@MarkusMeskanen: Kebebasan adalah bagi subclass untuk menggunakan nama yang sama seperti yang dilakukan superclass tanpa menabrak superclass - dengan kata lain, nama-nama gelap superclasses menjadi pribadi untuk dirinya sendiri.
Ethan Furman
311

Jawaban yang sangat bagus sejauh ini tetapi beberapa berita hilang. Satu garis bawah utama tidak persis hanya sebuah konvensi: jika Anda menggunakan from foobar import *, dan modul foobartidak mendefinisikan __all__daftar, nama yang diimpor dari modul tidak termasuk yang dengan garis bawah utama. Katakanlah itu sebagian besar adalah konvensi, karena kasus ini adalah sudut yang cukup tidak jelas ;-).

Konvensi terkemuka-garis bawah secara luas digunakan tidak hanya untuk pribadi nama, tetapi juga untuk apa C ++ sebut dilindungi yang - misalnya, nama-nama metode yang sepenuhnya dimaksudkan untuk menjadi ditimpa oleh subclass (bahkan yang harus menjadi ditimpa karena dalam kelas dasar mereka raise NotImplementedError! -) seringkali merupakan nama tunggal dengan garis bawah yang menunjukkan kode menggunakan instance kelas tersebut (atau subclass) yang mengatakan metode tidak dimaksudkan untuk dipanggil secara langsung.

Sebagai contoh, untuk membuat antrian thread-safe dengan disiplin antrian yang berbeda dari FIFO, seseorang mengimpor Antrian, subkelas Antrian. Antrian, dan mengganti metode seperti _getdan _put; "kode klien" tidak pernah memanggil metode ("pengait") itu, melainkan metode publik ("pengorganisasian") seperti putdan get(ini dikenal sebagai pola desain Metode Templat - lihat misalnya di sini untuk presentasi menarik berdasarkan video dari pembicaraan saya tentang masalah ini, dengan penambahan sinopsis transkrip).

Sunting: Tautan video dalam uraian pembicaraan sekarang terputus. Anda dapat menemukan dua video pertama di sini dan di sini .

Alex Martelli
sumber
1
Jadi, bagaimana Anda memutuskan apakah akan menggunakan _var_nameatau menggunakan var_name+ tidak termasuk dari __all__?
Endolith
3
@endolith Gunakan underscore terkemuka untuk memberi sinyal kepada pembaca kode Anda bahwa mereka mungkin tidak boleh menggunakan ini (misalnya, karena Anda mungkin mengubahnya di versi 2.0, atau bahkan 1.1); gunakan eksplisit __all__kapan pun Anda ingin membuat modul ini from spam import *ramah (termasuk di penerjemah interaktif). Jadi sebagian besar waktu, jawabannya adalah keduanya .
abarnert
@AlexMartelli Apakah aturan terkait impor ini dibahas secara hukum di suatu tempat di dokumen atau di tempat lain?
Vicrobot
1
Saya suka analogi C ++. Pertama, saya tidak suka ketika orang memanggil _ pribadi . Jelas saya berbicara tentang analogi, karena tidak ada yang benar-benar pribadi di Python. Ketika menyelam ke semantik saya akan mengatakan kita dapat mengikat _ke Jawa dilindungi karena diproklamasikan di Jawa berarti "kelas turunan dan / atau dalam paket yang sama". Ganti paket dengan modul karena PEP8 sudah memberi tahu kami bahwa _itu bukan hanya sebuah konvensi ketika berbicara tentang *impor dan Anda memilikinya. Dan pastinya __akan setara dengan private Java ketika berbicara tentang pengidentifikasi dalam suatu kelas.
Marius Mucenicu
2
Meskipun jawaban yang layak, itu juga sangat promosi sendiri.
Web dev hybrid
299

__foo__: ini hanya sebuah konvensi, cara bagi sistem Python untuk menggunakan nama yang tidak akan bertentangan dengan nama pengguna.

_foo: ini hanya sebuah konvensi, cara bagi pemrogram untuk menunjukkan bahwa variabel bersifat pribadi (apa pun artinya dengan Python).

__foo: ini memiliki arti nyata: interpreter menggantikan nama ini dengan _classname__foocara untuk memastikan bahwa nama tersebut tidak akan tumpang tindih dengan nama yang serupa di kelas lain.

Tidak ada bentuk garis bawah lainnya yang memiliki makna di dunia Python.

Tidak ada perbedaan antara kelas, variabel, global, dll dalam konvensi ini.

Ned Batchelder
sumber
6
Baru saja menemukan __foodan ingin tahu. Bagaimana bisa tumpang tindih dengan nama metode yang sama dengan Kelas lainnya? Maksud saya Anda masih harus mengaksesnya seperti instance.__foo()(jika tidak diubah namanya oleh penerjemah), bukan?
Bibhas Debnath
81
Orang ini menyatakan bahwa from module import *tidak mengimpor objek underscore-prefixed. Karena itu, _foolebih dari sekedar konvensi.
dotancohen
4
@Bibhas: jika kelas Bsubclass kelas A, dan keduanya mengimplementasikan foo(), maka B.foo()menimpa yang .foo()diwarisi dari A. Sebuah instance dari Bhanya akan dapat mengakses B.foo(), kecuali melalui super(B).foo().
nucky101
3
Untuk __dunder__nama, pemanggilan implisit melewati kamus instance, jadi itu mungkin sedikit lebih dari sekadar konvensi penamaan dalam beberapa kasus (lihat bagian metode pencarian khusus dalam model data).
wim
206

._variable semiprivate dan dimaksudkan hanya untuk konvensi

.__variablesering dianggap salah superprivat, sementara itu arti sebenarnya hanya untuk namemangle untuk mencegah akses tidak disengaja [1]

.__variable__ biasanya dicadangkan untuk metode atau variabel bawaan

Anda masih dapat mengakses .__mangledvariabel jika Anda sangat ingin. Double menggarisbawahi hanya namemangles, atau mengubah nama, variabel menjadi sepertiinstance._className__mangled

Contoh:

class Test(object):
    def __init__(self):
        self.__a = 'a'
        self._b = 'b'

>>> t = Test()
>>> t._b
'b'

t._b dapat diakses karena hanya disembunyikan oleh konvensi

>>> t.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'

t .__ a tidak ditemukan karena tidak ada lagi karena penamaan

>>> t._Test__a
'a'

Dengan mengakses instance._className__variablealih-alih hanya nama garis bawah ganda, Anda dapat mengakses nilai tersembunyi

NickCSE
sumber
tetapi bagaimana jika "__a" adalah variabel kelas, maka Anda tidak dapat mengaksesnya bahkan dengan instruksi dari python docs ..
Vitaliy Terziev
Tolong bisakah Anda memperbarui jawaban Anda dengan contoh garis bawah ganda sehubungan dengan warisan?
variabel
116

Garis bawah tunggal di awal:

Python tidak memiliki metode pribadi yang nyata. Sebaliknya, satu garis bawah pada awal metode atau nama atribut berarti Anda tidak boleh mengakses metode ini, karena itu bukan bagian dari API.

class BaseForm(StrAndUnicode):

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

    errors = property(_get_errors)

(Potongan kode ini diambil dari kode sumber Django: Django / form / forms.py). Dalam kode ini,errors adalah properti publik, tetapi metode yang dipanggil oleh properti ini, _get_errors, adalah "pribadi", jadi Anda tidak boleh mengaksesnya.

Dua garis bawah di awal:

Ini menyebabkan banyak kebingungan. Seharusnya tidak digunakan untuk membuat metode pribadi. Ini harus digunakan untuk menghindari metode Anda ditimpa oleh subclass atau diakses secara tidak sengaja. Mari kita lihat sebuah contoh:

class A(object):
    def __test(self):
        print "I'm a test method in class A"

    def test(self):
        self.__test()

a = A()
a.test()
# a.__test() # This fails with an AttributeError
a._A__test() # Works! We can access the mangled name directly!

Keluaran:

$ python test.py
I'm test method in class A
I'm test method in class A

Sekarang buat subclass B dan lakukan kustomisasi untuk metode __test

class B(A):
    def __test(self):
        print "I'm test method in class B"

b = B()
b.test()

Output akan ....

$ python test.py
I'm test method in class A

Seperti yang telah kita lihat, A.test () tidak memanggil metode B .__ test (), seperti yang kita harapkan. Tetapi pada kenyataannya, ini adalah perilaku yang benar untuk __. Dua metode yang disebut __test () secara otomatis diganti namanya (mangled) menjadi _A__test () dan _B__test (), sehingga mereka tidak sengaja menimpa. Ketika Anda membuat metode dimulai dengan __ itu berarti Anda tidak ingin siapa pun dapat menimpanya, dan Anda hanya bermaksud mengaksesnya dari dalam kelasnya sendiri.

Dua garis bawah di awal dan di akhir:

Ketika kita melihat metode seperti __this__, jangan menyebutnya. Ini adalah metode yang dimaksudkan untuk memanggil python, bukan Anda. Mari lihat:

>>> name = "test string"
>>> name.__len__()
11
>>> len(name)
11

>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60

Selalu ada fungsi operator atau asli yang memanggil metode ajaib ini. Terkadang itu hanya panggilan python kait dalam situasi tertentu. Sebagai contoh__init__() dipanggil ketika objek dibuat setelah __new__()dipanggil untuk membangun instance ...

Mari kita ambil contoh ...

class FalseCalculator(object):

    def __init__(self, number):
        self.number = number

    def __add__(self, number):
        return self.number - number

    def __sub__(self, number):
        return self.number + number

number = FalseCalculator(20)
print number + 10      # 10
print number - 20      # 40

Untuk detail lebih lanjut, lihat panduan PEP-8 . Untuk metode ajaib lainnya, lihat PDF ini .

PythonDev
sumber
1
Setelah mengedit jawaban ini sendiri, saya lebih suka stackoverflow.com/a/8689983/1048186
Josiah Yoder
Apa yang Anda maksud dengan "Seperti yang telah kita lihat, A.test () tidak memanggil metode B .__ test ()" - di mana Anda memanggil A.test ()?
variabel
18

Kadang-kadang Anda memiliki apa yang tampaknya menjadi tuple dengan garis bawah utama seperti pada

def foo(bar):
    return _('my_' + bar)

Dalam hal ini, yang terjadi adalah _ () adalah alias untuk fungsi pelokalan yang beroperasi pada teks untuk memasukkannya ke bahasa yang tepat, dll berdasarkan pada lokal. Misalnya, Sphinx melakukan ini, dan Anda akan menemukan di antara impor

from sphinx.locale import l_, _

dan di sphinx.locale, _ () ditugaskan sebagai alias dari beberapa fungsi lokalisasi.

Tim D
sumber
11

Karena begitu banyak orang merujuk pada pembicaraan Raymond , saya hanya akan membuatnya sedikit lebih mudah dengan menuliskan apa yang dikatakannya:

Maksud dari menggarisbawahi itu bukan tentang privasi. Niatnya adalah untuk menggunakannya persis seperti ini

class Circle(object):

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        p = self.__perimeter()
        r = p / math.pi / 2.0
        return math.pi * r ** 2.0

    def perimeter(self):
        return 2.0 * math.pi * self.radius

    __perimeter = perimeter  # local reference


class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

Sebenarnya kebalikan dari privasi, itu semua tentang kebebasan. Itu membuat subclass Anda bebas untuk mengganti salah satu metode tanpa melanggar yang lain .

Katakanlah Anda tidak menyimpan referensi lokal perimeterdi Circle. Sekarang, kelas turunan Tiremenimpa implementasi perimeter, tanpa menyentuh area. Ketika Anda menelepon Tire(5).area(), secara teori itu masih harus digunakan Circle.perimeteruntuk perhitungan, tetapi pada kenyataannya itu menggunakanTire.perimeter , yang bukan perilaku yang dimaksud. Itu sebabnya kami membutuhkan referensi lokal di Circle.

Tapi mengapa __perimeterbukannya _perimeter? Karena _perimetermasih memberikan kelas turunan kesempatan untuk menimpa:

class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

    _perimeter = perimeter

Double menggarisbawahi memiliki nama mangling, jadi ada kemungkinan sangat kecil bahwa referensi lokal di kelas induk mendapatkan override di kelas turunan. dengan demikian " membuat subclass Anda bebas untuk menimpa salah satu metode tanpa melanggar yang lain ".

Jika kelas Anda tidak akan diwarisi, atau metode overriding tidak merusak apa pun, maka Anda tidak perlu __double_leading_underscore.

laike9m
sumber
1
Terima kasih, slide tidak ditampilkan dengan benar sehingga saya akhirnya tidak mengerti mengapa kode saya akan gagal.
cgte
8

Jika seseorang benar-benar ingin membuat variabel read-only, IMHO cara terbaik adalah dengan menggunakan properti () dengan hanya diteruskan oleh pengambil. Dengan properti () kita dapat memiliki kontrol penuh atas data.

class PrivateVarC(object):

    def get_x(self):
        pass

    def set_x(self, val):
        pass

    rwvar = property(get_p, set_p)  

    ronly = property(get_p) 

Saya mengerti bahwa OP mengajukan pertanyaan yang sedikit berbeda, tetapi karena saya menemukan pertanyaan lain yang menanyakan 'bagaimana mengatur variabel privat' yang ditandai duplikat dengan yang satu ini, saya berpikir untuk menambahkan info tambahan ini di sini.

Dev Maha
sumber
8

Menuju ke https://dbader.org/blog/meaning-of-underscores-in-python

  • Single Leading Underscore (_var) : Konvensi penamaan yang menunjukkan nama dimaksudkan untuk penggunaan internal. Umumnya tidak dipaksakan oleh interpreter Python (kecuali dalam impor wildcard) dan dimaksudkan sebagai petunjuk untuk programmer saja.
  • Single Trailing Underscore (var_) : Digunakan oleh konvensi untuk menghindari konflik penamaan dengan kata kunci Python.
  • Double Leading Underscore (__ var) : Memicu mangling nama saat digunakan dalam konteks kelas. Dipaksa oleh juru bahasa Python.
  • Double Leading and Trailing Underscore (__ var__) : Mengindikasikan metode khusus yang didefinisikan oleh bahasa Python. Hindari skema penamaan ini untuk atribut Anda sendiri.
  • Single Underscore (_) : Kadang-kadang digunakan sebagai nama untuk variabel sementara atau tidak signifikan ("tidak peduli"). Juga: Hasil dari ekspresi terakhir dalam Python REPL.
Feuda
sumber
5

Jawaban yang bagus dan semuanya benar. Saya telah memberikan contoh sederhana bersama dengan definisi / makna yang sederhana.

Berarti:

some_variable --► itu publik siapa pun dapat melihat ini.

_some_variable --► itu publik siapa pun dapat melihat ini tetapi itu adalah konvensi untuk menunjukkan ... peringatan pribadi tidak ada penegakan yang dilakukan oleh Python.

__some_varaible --► Python menggantikan nama variabel dengan _classname__some_varaible (nama AKA mangling) dan itu mengurangi / menyembunyikan visibilitasnya dan lebih seperti variabel pribadi.

Jujur saja di sini Menurut dokumentasi Python

Variabel instance "" Privat "yang tidak dapat diakses kecuali dari dalam suatu objek tidak ada dalam Python"

Contoh:

class A():
    here="abc"
    _here="_abc"
    __here="__abc"


aObject=A()
print(aObject.here) 
print(aObject._here)
# now if we try to print __here then it will fail because it's not public variable 
#print(aObject.__here)
grepit
sumber
_ _some_varaible - .... dan itu mengurangi / menyembunyikan visibilitasnya dan lebih seperti variabel pribadi. Tidak, nama mangling adalah intinya, tidak menyembunyikan metode.
AMC
4

Garis bawah utama tunggal adalah konvensi. tidak ada perbedaan dari sudut pandang penerjemah jika apakah nama dimulai dengan garis bawah tunggal atau tidak.

Garisbaris depan dan belakang ganda digunakan untuk metode bawaan, seperti __init__,__bool__ , dll

Menggarisbawahi terkemuka ganda tanpa rekan trailing adalah sebuah konvensi juga, namun, metode kelas akan hancur oleh penerjemah. Untuk variabel atau nama fungsi dasar tidak ada perbedaan.

SilentGhost
sumber
3

Pertanyaan Anda bagus, ini bukan hanya tentang metode. Fungsi dan objek dalam modul biasanya diawali dengan satu garis bawah juga, dan dapat diawali oleh dua.

Tapi nama __double_underscore tidak di-namai dalam modul, misalnya. Apa yang terjadi adalah bahwa nama yang dimulai dengan satu (atau lebih) garis bawah tidak diimpor jika Anda mengimpor semua dari modul (dari impor modul *), juga tidak ada nama yang ditunjukkan dalam bantuan (modul).

u0b34a0f6ae
sumber
1
Selain itu, nama-nama yang dimulai dengan satu atau lebih garis bawah yang memiliki dua atau lebih garis bawah garis dasar berperilaku seperti nama lain lagi.
Bentley4
3

Berikut adalah contoh ilustrasi sederhana tentang bagaimana properti garis bawah ganda dapat memengaruhi kelas yang diwarisi. Jadi dengan pengaturan berikut:

class parent(object):
    __default = "parent"
    def __init__(self, name=None):
        self.default = name or self.__default

    @property
    def default(self):
        return self.__default

    @default.setter
    def default(self, value):
        self.__default = value


class child(parent):
    __default = "child"

jika Anda kemudian membuat turunan anak di python REPL, Anda akan melihat di bawah ini

child_a = child()
child_a.default            # 'parent'
child_a._child__default    # 'child'
child_a._parent__default   # 'parent'

child_b = child("orphan")
## this will show 
child_b.default            # 'orphan'
child_a._child__default    # 'child'
child_a._parent__default   # 'orphan'

Ini mungkin jelas bagi sebagian orang, tetapi itu membuat saya lengah di lingkungan yang jauh lebih kompleks

Marc
sumber
3

Variabel instance "Privat" yang tidak dapat diakses kecuali dari dalam suatu objek tidak ada dalam Python. Namun, ada konvensi yang diikuti oleh sebagian besar kode Python: nama diawali dengan garis bawah (misalnya _spam) harus diperlakukan sebagai bagian non-publik dari API (apakah itu fungsi, metode atau anggota data) . Ini harus dianggap sebagai detail implementasi dan dapat berubah tanpa pemberitahuan.

referensi https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

aptro
sumber
1
_ jauh lebih mirip dengan misalnya internal di c # kemudian ke pribadi. Double menggarisbawahi itu jauh lebih mirip dengan pribadi maka garis bawah adalah dengan pribadi saya akan mengatakan.
Ini
2

Mendapatkan fakta _ dan __ cukup mudah; jawaban lain mengekspresikannya dengan cukup baik. Penggunaannya jauh lebih sulit untuk ditentukan.

Ini adalah bagaimana saya melihatnya:

_

Harus digunakan untuk menunjukkan bahwa suatu fungsi bukan untuk penggunaan publik seperti misalnya API. Ini dan pembatasan impor membuatnya berperilaku seperti internaldi c #.

__

Harus digunakan untuk menghindari tabrakan nama dalam hirarki warisan dan untuk menghindari latebinding. Sama seperti pribadi di c #.

==>

Jika Anda ingin menunjukkan bahwa sesuatu bukan untuk penggunaan umum, tetapi harus bertindak seperti protecteddigunakan _. Jika Anda ingin menunjukkan bahwa sesuatu bukan untuk penggunaan umum, tetapi harus bertindak seperti privatedigunakan__ .

Ini juga kutipan yang sangat saya sukai:

Masalahnya adalah bahwa penulis kelas dapat secara sah berpikir "atribut / nama metode ini harus pribadi, hanya dapat diakses dari dalam definisi kelas ini" dan menggunakan konvensi __private. Tetapi kemudian, pengguna kelas itu dapat membuat subclass yang secara sah membutuhkan akses ke nama itu. Jadi, superclass harus dimodifikasi (yang mungkin sulit atau tidak mungkin), atau kode subclass harus menggunakan nama-nama yang dikacaukan secara manual (yang paling jelek dan rapuh).

Tetapi masalah dengan itu menurut saya bahwa jika tidak ada IDE yang memperingatkan Anda ketika Anda mengganti metode, menemukan kesalahan mungkin butuh waktu cukup lama jika Anda secara tidak sengaja mengganti metode dari kelas dasar.

Invader
sumber