Bir nesne adından önce tek ve çift alt çizginin anlamı nedir?


Yanıtlar:


1155

Tek Alt Çizgi

Bir sınıfta, önde gelen alt çizgiye sahip isimler, diğer programcılara, özniteliğin veya yöntemin özel olması gerektiğini belirtmek içindir. Ancak, adın kendisi ile özel bir şey yapılmaz.

PEP-8'den alıntı yapmak için :

_single_leading_underscore: zayıf "dahili kullanım" göstergesi. Örneğin from M import *, adı alt çizgi ile başlayan nesneleri içe aktarmaz.

Çift Alt Çizgi (Ad Yönetimi)

Gönderen Python docs :

Formun herhangi bir tanımlayıcısı __spam(en az iki önde gelen alt çizgi, en fazla bir arka alt çizgi) metin ile değiştirilir _classname__spam, burada classnameönde gelen alt çizgi (ler) çıkarılmış olan geçerli sınıf adıdır. Bu karıştırma, tanımlayıcının sözdizimsel konumu dikkate alınmadan yapılır, bu nedenle sınıf-özel durum ve sınıf değişkenlerini, yöntemleri, genel olarak depolanan değişkenleri ve hatta örneklerde depolanan değişkenleri tanımlamak için kullanılabilir. diğer sınıfların örnekleri için bu sınıfa özel.

Ve aynı sayfadan bir uyarı:

Ad yönetimi, sınıflara, türetilmiş sınıflar tarafından tanımlanan örnek değişkenleri hakkında endişelenmek zorunda kalmadan veya sınıf dışındaki kodla örnek değişkenleri ile mucking yapmak zorunda kalmadan "özel" örnek değişkenlerini ve yöntemlerini tanımlamak için kolay bir yol sağlamayı amaçlamaktadır. Mangling kurallarının çoğunlukla kazaları önlemek için tasarlandığını unutmayın; kararlı bir ruhun özel olarak kabul edilen bir değişkene erişmesi veya değiştirmesi yine de mümkündür.

Misal

>>> 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!'}

17
Sınıfta olmayan 2 alt çizgiyle bildirilen bir değişken adı varsa ne olur? Bu sadece normal bir değişken değil mi?
Dhruv Ramani

5
__değişken ad olarak basitçe çift ​​alt çizginin anlamı nedir? gibia, __ = foo()
AJ

104
Bu cevap son derece yanıltıcıdır, çünkü okuyucunun dunderscore'un örnek niteliklerini "süper özel" yapmak için kullanıldığına inanmasına neden olur. Bu değil gibi durum burada izah açıkça bu özel zıt olacak şekilde tasarlanmıştır ise o dunderscore incorrrectly, özel işareti üyelerine kullanılır devletler Raymond Hettinger tarafından.
Markus Meskanen

13
@MarkusMeskanen Ben katılmıyorum, cevap açıkça sınıf-özel değişkenler ve yöntemler örnekleri yapmak için bir dunderscore kullanımını belirtir. Dunderscore, bu yöntemleri ve değişkenleri alt sınıflar tarafından kolayca üzerine yazılacak şekilde (herkese açık hale getirecek şekilde) tasarlanırken, dunderscore kullanımı bu sınıf içinde kullanılmak üzere özel bir örneği korur.
arewm

6
@MarkusMeskanen: Özgürlük, alt sınıfların, üst sınıfı gizlemeksizin üst sınıfla aynı adları kullanmasıdır - diğer bir deyişle, üst sınıfların dunder adları kendilerine özel hale gelir.
Ethan Furman

311

Şu ana kadar mükemmel cevaplar var ama bazı çerezler eksik. Tek bir lider çizgi tam olarak değil sadece bir kongre: kullanırsanız from foobar import *, ve modül foobarbir tanımlamıyor __all__liste, modülün ithal isimler yok lider çizgi ile olanları içerir. Diyelim ki çoğunlukla bir kongre, çünkü bu dava oldukça belirsiz bir köşe ;-).

Lider-çizgi kongre yaygın değil sadece kullanılan özel adlar, aynı zamanda C ++ dediğimiz için korumalı , örneğin tam amaçlanan yöntemlerin isimleri alt sınıflara (hatta olanlar tarafından geçersiz kılınmış olması - olanlar var zira geçersiz kılınan olmak onlar raise NotImplementedError!) temel sınıf ! -) genellikle , söz konusu yöntemlerin doğrudan çağrılmadığını belirten , o sınıfın (veya alt sınıfların) örneklerini kullanarak kodlama yapmak için kullanılan tekli satırların alt çizgi isimleridir .

Örneğin, FIFO'dan farklı bir kuyruk disipline sahip bir iş parçacığı için güvenli kuyruk oluşturmak için, Kuyruk, Alt Sıra Queue.Queue alt sınıfları içe aktarılır ve _getve _put; "Müşteri kodu" asla bu ( "kanca") yöntemlerini çağırır fakat gibi oldukça ( "organize") Kamu yöntemler putve get(bu olarak bilinir Şablon Yöntemi tasarım deseni - örneğin bkz burada bir videoya dayanarak ilginç bir sunum için konuyla ilgili konuşmamın, konuşma metninin özetlerinin eklenmesiyle).

Düzenle: Görüşmelerin açıklamasındaki video bağlantıları artık bozuk. İlk iki videoyu burada ve burada bulabilirsiniz .


1
Peki + hariç tutup kullanmamaya _var_nameveya kullanmamaya nasıl karar verirsiniz ? var_name__all__
endolit

3
@ endolith Kod okuyucunuza bunu kullanmamaları gerektiğini belirtmek için önde gelen altçizgileri kullanın (örn. 2.0 ve hatta 1.1 sürümlerinde değiştirebilirsiniz); (etkileşimli yorumlayıcı dahil) __all__modülü from spam import *kolaylaştırmak istediğinizde açık kullanın . Yani çoğu zaman cevap her ikisidir .
abarnert

@AlexMartelli Bu içe aktarmayla ilgili kural, dokümanlar veya başka bir yerde yasal olarak tartışılıyor mu?
Vicrobot

1
C ++ benzetmesini seviyorum. İlk olarak, insanlar _ özel dedikleri zaman hoşlanmıyorum . Açıkçası benzetmelerden bahsediyorum, çünkü Python'da hiçbir şey özel değildir . Semantik içine dalış biz bağlayabiliriz söyleyebilirim zaman _Java'nın için korumalı beri proctected Java araçlarında "türetilmiş sınıfları ve / veya aynı paket içinde". PEP8 zaten ithalat _hakkında konuşurken sadece bir konvansiyon olmadığını *ve orada var olduğunu söylüyor çünkü paketi modülle değiştirin . Ve kesinlikle __Java en eşdeğer olacaktır özel bir sınıf içinde tanımlayıcıları bahsederken.
Marius Mucenicu

2
İyi bir cevap olsa da, aynı zamanda kendi kendini tanıtmaktır.
Hibrit web geliştirici

298

__foo__: Bu sadece bir kuraldır, Python sisteminin kullanıcı adlarıyla çakışmayan adları kullanmasının bir yoludur.

_foo: Bu sadece bir kural, programcının değişkenin özel olduğunu (Python'da ne anlama gelirse) belirtmesinin bir yoludur.

__foo: bunun gerçek bir anlamı vardır: yorumlayıcı _classname__foo, adın başka bir sınıfta benzer bir adla örtüşmemesini sağlamak için bu adı değiştirir.

Python dünyasında başka hiçbir alt çizginin anlamı yoktur.

Bu sözleşmelerde sınıf, değişken, küresel vb. Arasında bir fark yoktur.


6
Sadece rastladı __foove meraklıydı. Diğer Sınıflarla benzer yöntem adlarıyla nasıl çakışabilir? Yani hala instance.__foo()(tercüman tarafından yeniden adlandırılmamışsa) erişmek zorundasınız , değil mi?
Bibhas Debnath

81
Bu adamfrom module import * , altı çizili önekli nesneleri almadığını belirtir . Bu nedenle, _foosadece bir sözleşmeden daha fazlasıdır.
dotancohen

4
@Bibhas: sınıf Balt sınıfları sınıf Ave her ikisi de uygulanırsa foo(), devralınan öğeyi B.foo()geçersiz kılar . Üzerinden bir örnek yalnızca üzerinden erişebilecektir . .foo()ABB.foo()super(B).foo()
naught101

3
İçin __dunder__(bkz bazı durumlarda sadece bir adlandırma kuralı daha belki biraz daha yüzden isimler, örtük invokasyonlar, örnek sözlüğü atlamak özel yöntem arama datamodel bölümüne).
wim

206

._variable yarı özeldir ve sadece toplantı amaçlıdır

.__variablegenellikle yanlış özel olarak kabul edilir, ancak gerçek anlamı sadece yanlışlıkla erişimi önlemek için örnekleme yapmaktır [1]

.__variable__ genellikle yerleşik yöntemler veya değişkenler için ayrılmıştır

.__mangledUmutsuzca istiyorsanız değişkenlere hala erişebilirsiniz . İkili, değişkenin sadece adlandırmalarını veya adını değiştirir, değişkeniinstance._className__mangled

Misal:

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

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

t._b'ye erişilebilir çünkü yalnızca kongre ile gizlidir

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

t .__ a bulunamadı çünkü adlandırma nedeniyle artık mevcut değil

>>> t._Test__a
'a'

instance._className__variableYalnızca çift alt çizgi adı yerine erişerek gizli değere erişebilirsiniz


ama "__a" bir sınıf değişkeni olsaydı, python belgelerinden gelen talimatlarla bile erişemezsiniz ..
Vitaliy Terziev

Lütfen yanıtınızı kalıtımla ilgili bir çift alt çizgi örneğiyle güncelleyebilir misiniz?
değişken

116

Başlangıçta tek alt çizgi:

Python'un gerçek özel yöntemleri yoktur. Bunun yerine, bir yöntemin veya öznitelik adının başlangıcında bir alt çizgi, API'ya dahil olmadığı için bu yönteme erişmemeniz gerektiği anlamına gelir.

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)

(Bu kod snippet'i django kaynak kodundan alınmıştır: django / formlar / forms.py). Bu kodda, errorsgenel bir özelliktir, ancak bu özelliğin _get_errors olarak adlandırdığı yöntem "özel" dir, bu nedenle ona erişmemelisiniz.

Başlangıçta iki alt çizgi:

Bu çok karışıklığa neden olur. Özel bir yöntem oluşturmak için kullanılmamalıdır. Yönteminizin bir alt sınıf tarafından geçersiz kılınmasını veya yanlışlıkla erişilmesini önlemek için kullanılmalıdır. Bir örnek görelim:

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!

Çıktı:

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

Şimdi bir alt sınıf B oluşturun ve __test yöntemi için özelleştirme yapın

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

b = B()
b.test()

Çıktı ...

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

Gördüğümüz gibi, A.test () beklediğimiz gibi B .__ test () yöntemlerini çağırmadı. Ama aslında, bu __ için doğru davranış. __Test () adı verilen iki yöntem otomatik olarak _A__test () ve _B__test () olarak yeniden adlandırılır (karıştırılır), bu nedenle yanlışlıkla geçersiz kılmazlar. __ ile başlayan bir yöntem oluşturduğunuzda, kimsenin bunu geçersiz kılabilmesini istemediğiniz anlamına gelir ve yalnızca kendi sınıfının içinden erişmeyi düşünürsünüz.

Başlangıçta ve sonunda iki alt çizgi:

Gibi bir yöntem gördüğümüzde __this__, böyle çağırma. Bu, python'un sizi çağırması gereken bir yöntemdir. Hadi bir bakalım:

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

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

Her zaman bu sihirli yöntemleri çağıran bir operatör veya yerel işlev vardır. Bazen sadece belirli durumlarda bir kanca python çağırır. Örneğin __init__(), nesneyi __new__()oluşturmak için çağrıldıktan sonra nesne oluşturulduğunda çağrılır ...

Bir örnek verelim ...

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

Daha fazla ayrıntı için PEP-8 kılavuzuna bakın . Daha fazla sihirli yöntem için bu PDF'ye bakın .


1
Bu yanıtı kendim
Josiah Yoder

"Gördüğümüz gibi, A.test (), B .__ test () yöntemleri demedi" - A.test () adını nereden çağırdınız?
değişken

18

Bazen olduğu gibi önde gelen alt çizgiyle bir demet gibi görünen şeylere sahip olabilirsiniz.

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

Bu durumda, _ (), yerel ayara göre metni doğru dile yerleştirmek için çalışan bir yerelleştirme işlevinin takma adıdır. Örneğin, Sfenks bunu yapar ve ithalatlar arasında bulacaksınız

from sphinx.locale import l_, _

ve sphinx.locale içinde _ () bazı yerelleştirme işlevlerinin takma adı olarak atanır.


11

Pek çok insan Raymond'un konuşmasına atıfta bulunduğundan , söylediklerini yazarak biraz daha kolaylaştıracağım:

Çift alt çizginin niyeti gizlilikle ilgili değildi. Amaç tam olarak böyle kullanmaktı

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

Aslında mahremiyetin tam tersi, tamamen özgürlükle ilgili. Alt sınıflarınızı, diğerlerini bozmadan herhangi bir yöntemi geçersiz kılmaktan kurtarır .

Eğer yerel bir referansı tutmuyorum ki perimeteriçinde Circle. Artık türetilmiş bir sınıf , dokunmadan Tireuygulanmasını geçersiz kılar . Aradığınızda , teoride hala hesaplama için kullanıyor olmalı , ancak gerçekte , amaçlanan davranış değil, kullanıyor . Bu yüzden Circle'da yerel bir referansa ihtiyacımız var.perimeterareaTire(5).area()Circle.perimeterTire.perimeter

Ama neden __perimeteryerine _perimeter? Çünkü _perimeterhala türetilmiş sınıfa geçersiz kılma şansı veriyor:

class Tire(Circle):

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

    _perimeter = perimeter

Çift alt çizginin ad yönetimi vardır, bu nedenle üst sınıftaki yerel başvurunun türetilmiş sınıfta geçersiz kılma olasılığı çok düşüktür. böylece " alt sınıflarınızı herhangi bir yöntemi diğerlerini bozmadan geçersiz kılar ".

Sınıfınız devralınmayacaksa veya yöntem geçersiz kılma hiçbir şeyi bozmazsa, ihtiyacınız yoktur __double_leading_underscore.


1
Teşekkür ederim, slayt düzgün görüntülenmedi bu yüzden neden kodum başarısız olur unersanting sona erdi.
cgte

8

Eğer gerçekten bir değişken salt okunur yapmak istiyorsa, IMHO en iyi yol, özellik () 'i sadece alıcıyla geçirilmiş olarak kullanmak olacaktır. Property () ile veriler üzerinde tam kontrol sahibi olabiliriz.

class PrivateVarC(object):

    def get_x(self):
        pass

    def set_x(self, val):
        pass

    rwvar = property(get_p, set_p)  

    ronly = property(get_p) 

OP'nin biraz farklı bir soru sorduğunu anlıyorum ama 'özel değişkenlerin nasıl ayarlanacağını' soran başka bir soru bulduğumdan, bununla çift olarak işaretlendi, bu ek bilgiyi buraya eklemeyi düşündüm.


7

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

  • Tek Satır Aralığı (_var) : Bir adı gösteren adlandırma kuralı dahili kullanım içindir. Genellikle Python yorumlayıcısı tarafından zorlanmaz (joker karakterli ithalatlar hariç) ve yalnızca programcıya bir ipucu anlamına gelir.
  • Tek İzleyen Alt Çizgi (var_) : Kurallarla Python anahtar kelimeleriyle çakışmaları adlandırmamak için kullanılır.
  • Çift Satır Altı Alt Çizgisi (__ var) : Bir sınıf bağlamında kullanıldığında ad yönetimini tetikler. Python yorumlayıcısı tarafından uygulanıyor.
  • Çift Yönlendirme ve Sondaki Alt Çizgi (__ var__) : Python dili tarafından tanımlanan özel yöntemleri belirtir. Kendi adlandırmalarınız için bu adlandırma düzeninden kaçının.
  • Tek Alt Çizgi (_) : Bazen geçici veya önemsiz değişkenler için ad olarak kullanılır ("umursama"). Ayrıca: Python REPL'deki son ifadenin sonucu.

5

Harika cevaplar ve hepsi doğru, basit tanım / anlam ile birlikte basit bir örnek verdim.

Anlamı:

some_variable --► herkes bunu görebilir.

_some_variable --► herkes bunu görebilir herkese açık ama Python tarafından hiçbir yaptırım yapılmadığını bildiren özel ... uyarısı bir kuraldır .

__some_varaible --► Python, değişken adını _classname__some_varaible (AKA ad yönetimi) ile değiştirir ve görünürlüğünü azaltır / gizler ve daha çok özel değişken gibi olur.

Burada dürüst olmak gerekirse Python belgelerine göre

"Python'da bir nesnenin içinden erişilemeyen" özel "örnek değişkenleri yok"

Örnek:

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)

_ _some_varaible - .... ve görünürlüğünü azaltır / gizler ve daha çok özel değişken gibi olur. Hayır, isim mangling nokta, yöntemi gizlemiyor.
AMC

4

Tek önde gelen alt çizgiler bir kongre. isimlerin tek bir alt çizgi ile başlayıp başlamaması durumunda tercümanın bakış açısından bir fark yoktur.

Çift ön ve arka alt çizgi için kullanılan dahili gibi yöntemlerle, __init__, __bool__vs.

/ Y arka meslektaşları w çift gelen alt çizgi de, ancak, sınıf yöntemleri olacak bir Kongre olan karıştırılmış yorumlayıcı tarafından. Değişkenler veya temel işlev adları için hiçbir fark yoktur.


3

Sorunuz iyi, sadece yöntemlerle ilgili değil. Modüllerdeki işlevler ve nesneler genellikle bir alt çizgi ile önek olarak eklenir ve iki ile önek eklenebilir.

Ancak __double_underscore adları, örneğin modüllerde isimlendirilmiş değildir. Bir veya daha fazla alt çizgiyle başlayan adların tümü bir modülden (modül içe aktarma *) içe aktarılırsa veya yardımda (modül) gösterilen adlar içe aktarılmaz.


1
Ayrıca, iki veya daha fazla alt çizgiye sahip bir veya daha fazla alt çizgiyle başlayan adlar, başka bir ad gibi davranır.
Bentley4

3

Çift alt çizgi özelliklerinin miras alınan bir sınıfı nasıl etkileyebileceğine dair basit bir açıklayıcı örnek. Yani aşağıdaki kurulumla:

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"

daha sonra python REPL'de bir alt örnek oluşturursanız, aşağıdakileri görürsünüz

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'

Bu bazıları için açık olabilir, ama çok daha karmaşık bir ortamda beni hazırlıksız yakaladı


3

Bir nesnenin içinden erişilemeyen “özel” örnek değişkenleri Python'da mevcut değildir. Bununla birlikte, çoğu Python kodunun izlediği bir kural vardır: bir alt çizgi (örn. _Spam) önekine sahip bir ad, API'nın herkese açık olmayan bir parçası olarak ele alınmalıdır (bir işlev, yöntem veya veri üyesi olsun) . Bir uygulama detayı olarak düşünülmeli ve haber verilmeden değiştirilebilir.

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


1
_, örneğin c # 'daki internal'e ve özel olana çok daha benzer. Çift alt çizgi özel çok daha sonra alt çizgi özel diyebilirim diyebilirim.
Ini

2

_ Ve __ 'nin gerçeklerini öğrenmek oldukça kolaydır; diğer cevaplar onları oldukça iyi ifade ediyor. Kullanımın belirlenmesi çok daha zordur.

Ben böyle görüyorum:

_

Bir işlevin örneğin bir API gibi genel kullanım için olmadığını belirtmek için kullanılmalıdır. Bu ve alma kısıtlaması internalc # 'daki gibi davranmasını sağlar .

__

Kalıtım hirarşisinde isim çarpışmasını önlemek ve geç bağlanmayı önlemek için kullanılmalıdır. Çok c # özel gibi.

==>

Bir şeyin kamusal kullanım için olmadığını, ancak protectedkullanım gibi davranması gerektiğini belirtmek istiyorsanız _. Bir şeyin kamusal kullanım için olmadığını, ancak privatekullanım gibi davranması gerektiğini belirtmek istiyorsanız __.

Bu da çok sevdiğim bir teklif:

Sorun şu ki, bir sınıfın yazarı meşru bir şekilde "bu öznitelik / yöntem adı özel olmalı, sadece bu sınıf tanımından erişilebilir" ve __private kuralını kullanabilir. Ancak daha sonra, bu sınıfın bir kullanıcısı, bu ada meşru bir şekilde erişmesi gereken bir alt sınıf oluşturabilir. Bu yüzden ya üst sınıfın değiştirilmesi (zor ya da imkansız olabilir) ya da alt sınıf kodunun elle karıştırılmış adları (en iyi çirkin ve kırılgan) kullanması gerekir.

Ancak, bence sorun, yöntemleri geçersiz kıldığınızda sizi uyaran bir IDE yoksa, bir taban sınıfından bir yöntemi yanlışlıkla geçersiz kıldığınızda hatayı bulmak biraz zaman alabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.