Python'un sınıflarda “özel” değişkenleri var mı?


578

Java dünyasından geliyorum ve Bruce Eckels'in Python 3 Patterns, Recipes and Idioms'u okuyorum .

Sınıflar hakkında okurken, Python'da örnek değişkenleri bildirmeye gerek olmadığını söyler. Onları sadece yapıcıda kullanırsınız ve patlar, onlar oradadır.

Yani mesela:

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())

Bu doğruysa, sınıfın herhangi bir nesnesi sınıfın dışındaki Simpledeğişkenin değerini değiştirebilir s.

Örneğin:

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

Java'da, herkese açık / özel / korunan değişkenler hakkında bilgi verildi. Bu anahtar kelimeler anlamlıdır, çünkü bazen sınıf dışında kimsenin erişemeyeceği bir sınıfta değişkenler istersiniz.

Python'da neden gerekli değil?


17
Sınıf değişkenleri değil, örnek değişkenleri mi demek istediniz ?
PaulMcG

13
Özellikleri kontrol etmelisiniz: docs.python.org/library/functions.html#property . Sadece alıcıyı kullanın ve değişkeniniz korunacaktır.
rubik

Kısa ve net bir cevap burada . Umarım bu yardımcı olacak.
Premkumar chalmeti

Yanıtlar:


962

Kültürel. Python'da, diğer sınıfların örneğine veya sınıf değişkenlerine yazmazsınız. Java'da, gerçekten istediğinizde hiçbir şey aynı şeyi yapmanıza engel olmaz - sonuçta, aynı etkiyi elde etmek için sınıfın kaynağını her zaman düzenleyebilirsiniz. Python, bu güvenlik iddiasını düşürür ve programcıları sorumlu olmaya teşvik eder. Uygulamada, bu çok güzel çalışıyor.

Herhangi bir nedenle özel değişkenleri taklit etmek istiyorsanız, her zaman PEP 8__ önekini kullanabilirsiniz . Python değişkenlerin isimleri gibi mangles onlar her ne kadar (onları içeren sınıf dışındaki koda kolayca görülebilir değiliz ki olabilir sadece gibisiniz belirlenen yeterli size eğer etrafında almak olabilir bunda çalışıyorsanız Java'nın korumaları etrafında almak ).__foo

Aynı kural gereği, _önek teknik olarak engellenmese bile uzak durmak demektir . Başka bir sınıfın __fooveya gibi görünen değişkenleriyle oynamazsınız _bar.


14
Mantıklı. Ancak, java'da sınıf dışındaki özel değişkenlere erişmenin herhangi bir yolu olduğunu düşünmüyorum (aslında ders sınıfının kaynağını değiştirmek dışında). Var mı?
Omnipresent

168
Python yolunu tercih etme eğilimindeyim, ama java yolunun sizin kadar anlamsız olduğunu düşünmüyorum. Özel bir şeyi beyan etmek, hızlı bir şekilde kodu okuyan birine çok faydalı bir şey söyler: bu alan yalnızca bu sınıf içinde değiştirilir.
Ned

67
@Onnipresent, yansımayı kullanarak yapabilirsiniz.
rapadura

76
Bunu açıklığa kavuşturayım, bu yüzden Python genel veya özel nitelikleri uygulamıyor, çünkü "bu bir güvenlik iddiası ve programcıları sorumlu olmaya teşvik ediyor", ancak topluluk özel değişkenleri ve yöntemleri göstermek için "_" kullanımını teşvik ediyor mu? Belki python kesinlikle kamu ve özel hayır olmalıdır? Temel amacı, bir sınıfla etkileşim kurmak için hangi API'yı kullanmanız gerektiğini söylemektir. Size bu yöntemleri kullanmanızı ve bunları kullanmamanızı söyleyen bir belge görevi görürler. Onlar bir "güvenlik iddiası" değil, hatta IDE tarafından size rehberlik etmek için kullanılabilecek API belgeleri!
PedroD

19
Bu iyi bir cevaptır ve muhakemeniz kesinlikle geçerlidir, ancak bir hususu kabul etmiyorum. Erişim değiştiricilerin amacı hiçbir zaman güvenlik olmamıştır . Daha ziyade, bir sınıfın hangi bölümlerinin dahili olarak kabul edildiğini ve bu sınıfın dış kullanıcılarına maruz bırakıldığını açıkça sınırlamanın (ve büyük ölçüde zorlamanın) bir aracıdır. Sözleşmeler (kültür), erişim değiştiricilere kesinlikle geçerli bir alternatiftir ve her iki yöntemin de artıları ve eksileri vardır, ancak dil düzeyinde erişim değiştiricilerinin her zamanki anlamda "güvenli" olmalarını amaçlamak yanıltıcıdır. sözcüğü.
devios1

159

Python'daki özel değişkenler aşağı yukarı bir hack'tir: yorumlayıcı değişkeni kasıtlı olarak yeniden adlandırır.

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

Şimdi, __varsınıf tanımının dışına erişmeye çalışırsanız başarısız olur:

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

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

Ancak bununla kolayca kurtulabilirsiniz:

 >>>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

Muhtemelen OOP'deki yöntemler böyle çağrılan biliyoruz: x.printVar() => A.printVar(x)eğer A.printVar()bazı alanını erişebilir x, bu alan aynı zamanda ulaşılabilir dışarıdan A.printVar() ... sonuçta fonksiyonları kullanırlılık oluşturulur, içeride açıklamalara verilen özel bir güç vardır.

İlgili bir derleyici olduğunda oyun farklıdır ( gizlilik derleyici düzeyinde bir kavramdır ). Erişim denetimi değiştiricileri ile sınıf tanımını bilir, böylece derleme zamanında kurallara uyulmadığı zaman hata verebilir


3
Kısacası, bu kapsülleme değil
watashiSHUN

2
PHP'nin aptal özel değişkenleri ile benzer bir şeye sahip olup olmadığını merak ediyorum - özel değişkenler gerçekten yorumlanmış dilde bir anlam ifade etmediği için - derlenmiş değilse x değişkeninin gizli olduğunu bilmek hangi optimizasyonu yapabilir?
NoBugs

1
Özel değişkenlerin modelini nasıl rastgele seçebiliriz?
16ron

@crisron same question
IanS

5
@watashiSHUN "kısacası, bu kapsülleme değil" => evet öyle. Kapsülleme yalnızca ortak API'yı kullanmakla ilgilidir, bu nedenle istemci kodu uygulama değişikliklerinden korunur. Adlandırma kuralları, API'nin ne olduğunu ve uygulamanın ne olduğunu anlatmanın mükemmel bir yoludur ve mesele sadece işe yarıyor olmasıdır.
bruno desthuilliers

30

Yukarıdaki yorumların çoğunda doğru bir şekilde belirtildiği gibi, Erişim Değiştiricilerin ana hedefini unutmayalım: Kod kullanıcılarının neyin değişmesi ve neyin değişmemesi gerektiğini anlamasına yardımcı olmak. Özel bir alan gördüğünüzde onunla uğraşmazsınız. Yani çoğunlukla Python'da _ ve __ tarafından kolayca elde edilen sözdizimsel şekerdir.


4
Bence bu en az onun kadar önemli bir nokta. Kod hata ayıklama yaparken (Biliyorum, hataları tanıtmak için bir zayıfım), hangi sınıfların üye değişkenini değiştirebileceğini bilmek hata ayıklama işlemini basitleştirir. En azından, değişken bir kapsam tarafından korunuyorsa. Benzer bir kavram, C ++ 'da const fonksiyonlarıdır. Ben biliyorum o üye değişkenler orada değişti ve hatta kötü bir değişken ayarı potansiyel nedeni olarak bu yöntemin bakma o kadar değildi. Sınıf uzantıları / ekleme özelliklerinin daha sonra geliştirilmesini sağlayabilmesine rağmen, kodun görünürlüğünü sınırlamak hata ayıklamayı kolaylaştırır.
MrMas

18

Alt çizgi sözleşmesinde özel değişkenlerin bir varyasyonu vardır.

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'

Bazı ince farklılıklar vardır, ancak ideolojik saflığı modellemek amacıyla, yeterince iyi.

Bu kavramı @özel dekoratörlere daha yakından uygulayan örnekler var, ama YMMV. Tartışmalı olarak meta kullanan bir sınıf tanımı da yazılabilir


14
Bunun partiye oldukça geç olduğunu anlıyorum, ancak bu bağlantı google'da sorunu google'da gösterir. Bu hikayenin tamamını anlatmıyor. __xsınıf içindeki bir değişken Aaslında derleyici tarafından yeniden yazıldığından _A__x, hala tamamen özel değildir ve hala erişilebilir.
Zorf

1
Elbette, adında bir değişken görürsem _A__x, ona dokunmayacağım. Bulaşıcı olabilir. Cehennemden kaçacağım.
Mateen Ulhaq

Doğru bir özel değil emin . Ancak, C ++ ve Java (vb) 'de zorla zorlanan özel, derleyici optimizasyonu için temel neden, Python'da gerçekten mevcut değildir, bu nedenle özel konvansiyonel yeterince iyidir. Python sözleşmesi genellikle gözetim altında olmadan davranacağınıza güvenmesidir. (Ve bu bir acemi tuzağı, ama biliyorsun, sadece sınıf tasarımı ve tüketimi hakkında düşünceli ol)
Shayne

14

"Java'da bizlere kamu / özel / korunan değişkenler öğretildi"

"Python'da neden gerekli değil?"

Aynı nedenden dolayı Java'da gerekli değildir .

Kullanmakta özgürsünüz - ya da kullanmamakta privateve protected.

Bir Python ve Java programcısı olarak bunu buldum privateve protectedçok, çok önemli tasarım kavramları oldum . Ama pratik bir konu olarak, on binlerce Java ve Python satırında, aslında hiç kullanmadım privateya da protected.

Neden olmasın?

İşte sorum "kimden korunuyor?"

Ekibimdeki diğer programcılar? Kaynağı var. Korunması, değiştirebilecekleri zaman ne anlama geliyor?

Diğer takımlardaki diğer programcılar? Aynı şirket için çalışıyorlar. Bir telefon görüşmesi ile kaynağı alabilirler.

Müşteriler? Kiralık işe programlama (genellikle). İstemciler (genellikle) koda sahiptir.

Peki, kimden - tam olarak - onu koruyorum?


118
-1: Porculus'a katılıyorum. Bu, erişimi yasaklamak veya bir şeyi gizlemekle ilgili değil, örtülü API belgeleriyle ilgili. Geliştiriciler ve derleyiciler / tercüman / kod denetleyici, hangi üyelerin kullanılması gerektiğini ve hangilerinin dokunulmaması gerektiğini (veya en azından dikkatle) kolayca görebilir. Çoğu durumda, bir sınıfın veya modülün tüm üyelerinin halka açık olması korkunç bir karışıklık olurdu. Bir hizmet olarak özel / korunan / kamu üyelerinin ayrımını düşünün: "Hey, bu üyeler dahili olarak kullanılırken önemlidir ve muhtemelen sizin için yararlı değildir."
Oben Sonne

7
@ S.Lott: API belgelerinin daha yüksek önceliğe sahip olduğunu ve genellikle bir API'nın kullanım amaçlarını bildirmenin tek yolu olduğunu kabul ediyorum. Ancak bazen üye adları ve görünürlük (özel / kamu açısından) kendi başlarına yeterince konuşur. Ayrıca, örtük dokümantasyon fikrinin API incelemesi olmadan editörlerde iyi çalışmadığını ancak IDE'lerin kod tamamlama konusunda gerçekten yararlı olduğunu belirtmek isterim. API belgelerini bir süre önce okuduğunuzu varsayarsak, bir sınıfı nasıl kullanacağınızı hatırlamanıza yardımcı olur. Özel ve kamusal üyeler arasında bir ayrım olmasaydı işler o kadar akıllı olmazdı.
Oben Sonne

21
Tartışmaya geç, ancak Porculus ve Oben'in burada istediği her şey "alt çizgiyle önek" konvansiyonu (ve bu sözleşmenin derleyici uygulamasının neden olabileceği zarar olmadan) mükemmel bir şekilde ele alınır
ncoghlan

38
@ S.Lott Ben bir piton adamı değilim, bu yüzden bu açıdan yorum yapmayacağım. Ancak bir java geliştiricisi olarak bu gerçekten korkunç bir tavsiye. -1
dbyrne

11
Vay. Bu noktayı tamamen özlüyorsunuz, çok kötü bir tavsiyede bulunuyorsunuz, bu noktada sizinle aynı fikirde olmayan herkese hakaret ediyorsunuz, ancak yine de bu "cevap" için rozetler ve 1000'den fazla itibar puanı alıyorsunuz.
Eric Duminil

12

Daha önce de belirtildiği gibi, bir değişkenin veya yöntemin alt çizgi ile önek olarak özel olduğunu belirtebilirsiniz. Bunun yeterli olduğunu düşünmüyorsanız, propertydekoratörü her zaman kullanabilirsiniz . İşte bir örnek:

class Foo:

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

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

Bu şekilde, referans veren biri bar aslında bardeğişkenin kendisinden ziyade işlevin dönüş değerini ifade eder ve bu nedenle erişilebilir ancak değiştirilemez. Ancak, birisi gerçekten isterse, sadece kullanabilir _barve ona yeni bir değer atayabilir. Birisinin, tekrar tekrar söylendiği gibi, gizlemek istediğiniz değişkenlere ve yöntemlere erişmesini önlemenin kesin bir yolu yoktur. Ancak, propertybir değişkenin düzenlenmeyeceğini gönderebileceğiniz en açık mesaj kullanmaktır . propertyburada açıklandığı gibi daha karmaşık alıcı / ayarlayıcı / silici erişim yolları için de kullanılabilir: https://docs.python.org/3/library/functions.html#property


Django da bunu takdir ediyor.
babygame0ver

10

Python, sınıf adını iki alt çizgiyle başlayan tüm tanımlayıcılara otomatik olarak hazırlayan bir özellik sayesinde özel tanımlayıcılar için sınırlı desteğe sahiptir. Bu, programcı için çoğunlukla şeffaftır, ancak net etki, bu şekilde adlandırılan değişkenlerin özel değişkenler olarak kullanılabilmesidir.

Buraya bakınBununla ilgili daha fazla .

Genel olarak, Python'un nesne yönelimi uygulaması diğer dillerle karşılaştırıldığında biraz ilkeldir. Ama aslında bundan zevk alıyorum. Kavramsal olarak çok basit bir uygulamadır ve dilin dinamik stiline çok uygundur.


Evet. Güzelliği, python'un metaprogramlama yetenekleri, eğer isterseniz fantezi şeyleri uygulayabileceğiniz anlamına gelir (Ve @ private / @ korumalı / etc dekoratörleri ve şeyleri uygulayan kütüphaneler var. Cehennem JS tarzı prototip sınıflarını imleyen bir kütüphane bile gördüm. hiçbir lanet olası aklı neden), ama pratikte bu sadece gerekli değil .. "python / js / lisp ne olursa olsun" meme nefret ediyorum çünkü neredeyse hiç doğru değil, ama python metaprogramlama pirzola paylaşıyor 'basit sözdizimi ile birlikte 'bu dille
Shayne

8

Özel değişkenleri kullandığım tek zaman, değişkene yazarken veya değişkenten okurken başka şeyler yapmam gerektiğinde ve bu nedenle bir ayarlayıcı ve / veya alıcı kullanımını zorlamam gerektiğidir.

Yine bu, daha önce de belirtildiği gibi kültüre gider. Diğer sınıf değişkenlerinin okunması ve yazılmasının herkes için ücretsiz olduğu projeler üzerinde çalışıyorum. Bir uygulama kullanımdan kaldırıldığında, bu işlevi kullanan tüm kod yollarını tanımlamak çok daha uzun sürdü. Ayarlayıcıların ve alıcıların kullanımı zorunlu kılındığında, kullanımdan kaldırılmış yöntemin çağrıldığını ve onu çağıran kod yolunu tanımlamak için kolayca bir hata ayıklama ifadesi yazılabilir.

Herkesin bir uzantı yazabileceği bir projedeyken, birkaç sürümde kaybolacak kullanımdan kaldırılmış yöntemler hakkında kullanıcıları bilgilendirmek, bu nedenle yükseltme sonrasında modül kopmasını minimumda tutmak için çok önemlidir.

Yani cevabım; siz ve meslektaşlarınız basit bir kod kümesine sahipseniz, sınıf değişkenlerini korumak her zaman gerekli değildir. Genişletilebilir bir sistem yazıyorsanız, kodda tüm uzantılar tarafından yakalanması gereken çekirdekte değişiklikler yapıldığında bu zorunlu hale gelir.


8

Konu "diriliş" için üzgünüm çocuklar, ama, bu birine yardımcı olacağını umuyoruz:

Python3'te sadece Java gibi sınıf niteliklerini "kapsüllemek" istiyorsanız, aynı şeyi şu şekilde yapabilirsiniz:

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())

Bunu başlatmak için şunları yapın:

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

Unutmayın: print(ss.__s)bir hata verir.

Uygulamada, Python3 genel öznitelik adını gizleyecektir. Bunu, Java'daki gibi "özel" bir öznitelik gibi çevirmek. Özelliğin adı hala küreseldir, ancak erişilemez bir şekilde, diğer dillerdeki özel bir özellik gibi.

Ama bundan korkma. Önemli değil. İşi de yapıyor. ;)


2
bu Python 1.5.2 IIRC'den beri var olmuştur ve yine de özelliğe karıştırılmış adıyla erişilmesini engellememektedir.
bruno desthuilliers

7

özel ve korunan kavramlar çok önemlidir. Ancak python - sadece geliştirme için kısıtlı kaynaklarla prototipleme ve hızlı geliştirme için bir araçtır, bu nedenle koruma seviyelerinin bazıları python'da çok katı değildir. Sınıf üyesinde "__" kullanabilirsiniz, düzgün çalışır, ancak yeterince iyi görünmez - bu alana her erişim bu karakterleri içerir.

Ayrıca, python OOP konseptinin mükemmel, smaltalk veya yakut olmadığını, saf OOP konseptine çok daha yakın olduğunu fark edebilirsiniz. C # veya Java bile daha yakın.

Python çok iyi bir araç. Ancak basitleştirilmiş OOP dilidir. Sözdizimsel ve kavramsal olarak basitleştirilmiştir. Python varlığının temel amacı, geliştiricilere çok hızlı bir şekilde yüksek soyutlama seviyesine sahip kolay okunabilir kod yazma imkanı sunmaktır.


Özel ve Korumalı arka kısmı önemlidir, derleyici olarak, derleyicinin özel yönteme doğrudan aramalar oluşturabilmesi, ancak genel yöntemler için bir arama tablosuna güvenmesi gerektiğidir. Bu sadece dinamik dillerle ilgili bir sorun değildir. Son olarak C ++ gibi diller, kalıtım ve yöntem çözümlemesi için çıkarımlar içermektedir. Python ve Ruby, OO'nun çok benzer uygulamalarına sahiptir, bu nedenle karşılaştırma anlamsızdır. Smalltalk aslında genel / özel mesaj kavramına sahip değildir. Bir kategori olarak özel eklemek için ücretsiz, ama tamamen danışma.
Shayne

Daha ileri sürmek için. Kodlayıcı bir hijyen bakış açısıyla frorm, evet kapsülleme için önemlidir, ancak bunun için gerekli değildir ve bu nedenle @private (vb.) Dekoratörler her şeyden daha fazla tavsiye niteliğindedir, ancak özel / genel bir statik olmayan bir dil, java veya c gibi derlenmiş bir dilde olduğu gibi derin bir düzeyde uygulanmamış
Shayne

4

Python'un C ++ veya Java gibi herhangi bir özel değişkeni yoktur. İsterseniz, herhangi bir üye değişkenine istediğiniz zaman erişebilirsiniz. Ancak, Python'da özel değişkenlere ihtiyacınız yoktur, çünkü Python'da sınıflarınızın üye değişkenlerini ortaya çıkarmak kötü değildir. Bir üye değişkeni kapsülleme gereksiniminiz varsa, bunu daha sonra varolan istemci kodunu bozmadan "@property" kullanarak yapabilirsiniz.

Python'da tek bir alt çizgi "_", bir yöntemin veya değişkenin bir sınıfın genel api'sinin bir parçası olarak değerlendirilmediğini ve api'nin bu kısmının farklı versiyonlar arasında değişebileceğini belirtmek için kullanılır. Bu yöntemleri / değişkenleri kullanabilirsiniz, ancak bu sınıfın daha yeni bir sürümünü kullanırsanız kodunuz bozulabilir.

"__" çift alt çizgisi "özel değişken" anlamına gelmez. Bunu, "sınıf yerel" olan ve alt sınıflar tarafından kolayca geçersiz kılınamayan değişkenleri tanımlamak için kullanırsınız. Değişkenlerin ismini değiştirir.

Örneğin:

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 .__ foobar'ın adı otomatik olarak A sınıfındaki self._A__foobar ile karıştırılır. B sınıfında self._B__foobar ile karıştırılır. Böylece her alt sınıf, ebeveyn değişken (ler) ini geçersiz kılmaksızın kendi değişken __foobar'ını tanımlayabilir. Ancak hiçbir şey, çift alt çizgi ile başlayan değişkenlere erişmenizi engellemez. Ancak, ad yönetimi bu değişkenleri / yöntemleri tesadüfen çağırmanızı önler.

Raymond Hettingers'ın Pycon 2013'ten (Youtube'da mevcut olması gerekir) "Pythons sınıfı geliştirme araç takımı" nı konuşmasını izlemenizi şiddetle tavsiye ederim.


Bu konuşmaya bakacağım. @propertyŞey standart Python'un bir parçası mı , yoksa bir IDE'ye mi özgü?
bballdave025

Python 2.6'dan beri standardın bir parçası. Daha eski bir sürüm kullanmanız gerekiyorsa property, python 2.2'den beri kullanılabilen yerleşik işlevi kullanma olasılığı hala vardır
2.2'den Hatatister

0

Aslında C#bu basit numarayı kullanarak bir alıcı ve ayarlayıcıyı simüle edebilirsiniz :

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

Sonra aşağıdaki gibi kullanın C#:

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

Sadece bir get / set rolü oynayacak bir işlevde statik bir yerel değişken bildirir, çünkü bir değişkeni bir sınıf veya dosya için global yapmadan get ve set yöntemleri aracılığıyla paylaşmanın tek yolu budur.

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.