Python'un 'özel' yöntemleri neden özel değil?


658

Python bize bu gibi isme çift çizgi ekleyerek bu bir sınıf içinde 'özel' yöntem ve değişkenleri oluşturma olanağı verir: __myPrivateMethod(). Öyleyse, kişi bunu nasıl açıklayabilir?

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

Anlaşma ne?!

Bunu tam olarak anlamayanlar için biraz açıklayacağım.

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()

Yaptığım şey, ortak bir yöntem ve özel bir yöntemle bir sınıf oluşturmak ve örneklemek.

Sonra onun genel yöntemini çağırıyorum.

>>> obj.myPublicMethod()
public method

Sonra, kendi özel yöntemini deniyorum ve çağırıyorum.

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

Burada her şey iyi görünüyor; onu arayamayız. Aslında 'özel'. Aslında öyle değil. Nesne üzerinde dir () komutu çalıştırıldığında , python'un tüm 'özel' yöntemleriniz için sihirli bir şekilde oluşturduğu yeni bir büyülü yöntem ortaya çıkar.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

Bu yeni yöntemin adı her zaman bir alt çizgi, ardından sınıf adı ve ardından yöntem adıdır.

>>> obj._MyClass__myPrivateMethod()
this is private!!

Kapsülleme için çok mu, ha?

Her durumda, Python'un kapsüllemeyi desteklemediğini her zaman duymuştum, neden denesin ki? Ne oluyor?


18
Yansıtma kullanıyorsanız Java veya C # için de aynıdır (bir şekilde orada ne yaptığınızdır).
0x2434D53

4
Birim Testi amacıyla oluşturulmuştur, bu nedenle sınıfınızın özel yöntemlerini dışarıdan test etmek için bu "hack" i kullanabilirsiniz.
waas1919

16
Özel yöntemleri test etmek bir model değil mi? Özel yöntemler bazı genel yöntemlerde kullanılacaktır, aksi takdirde sonsuza kadar kullanılmaz. Özel yöntemleri test etmenin doğru yolu (ThoughtWorks'ten bugüne kadar öğrendiğime dayanarak), herkese açık yöntemleri yalnızca tüm vakaları kapsayan testler yazmanızdır. Bu işe yararsa, özel yöntemleri dışarıdan test etmeniz gerekmez.
Vishnu Narang

3
@VishnuNarang: Evet, bu sık sık öğretilen şey. Ama her zaman olduğu gibi, " her zaman bunu yap, asla yap " neredeyse "dini" yaklaşımı, "asla" nin iyi olduğu tek şeydir. Birim testleri "yalnızca" regresyon testleri veya genel API testi için kullanılıyorsa, ayrıcalıkları test etmenize gerek yoktur. Ancak, birim test güdümlü geliştirme yaparsanız, geliştirme sırasında özel yöntemleri test etmek için iyi nedenler vardır (örneğin, genel arayüz aracılığıyla bazı olağandışı / aşırı parametreleri taklit etmek zor olduğunda). Bazı diller / birim test ortamları bunu yapmanıza izin vermez, IMHO iyi değildir.
Marco Freudenberger

5
@MarcoFreudenberger Ne demek istediğini anlıyorum. Birim test odaklı geliştirme konusunda deneyimim var. Genellikle parametreleri taklit etmek zorlaştığında, çoğu zaman tasarımı değiştirerek ve geliştirerek çözülür. Özel yöntemlerin test edilmesini önlemek için tasarımın mükemmel olduğu ve hala birim testinin son derece zor olduğu bir senaryoya rastlamıyorum. Bu tür davalara bakacağım. Teşekkürler. Anlamanıza yardımcı olması için başınızın üstünden bir senaryo paylaşabiliyorsanız çok memnun olurum.
Vishnu Narang

Yanıtlar:


592

Şifreleme adı, alt sınıfların üst sınıflarının özel yöntemlerini ve özelliklerini yanlışlıkla geçersiz kılmalarını sağlamak için kullanılır. Dışarıdan kasıtlı erişimi önlemek için tasarlanmamıştır.

Örneğin:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

Tabii ki, iki farklı sınıf aynı ada sahipse bozulur.


12
docs.python.org/2/tutorial/classes.html . Bölüm: 9.6 Özel değişkenler ve sınıf-yerel referanslar.
gjain

72
Kaydırma / arama yapmak için çok tembel olanlar için: Bölüm 9.6 doğrudan bağlantı
cod3monk3y

3
Değişkenin özel olarak kabul edilmesi gerektiğini belirtmek için tek bir alt çizgi koymalısınız. Yine, bu birisinin buna gerçekten erişmesini engellemez.
igon

14
Guido bu soruyu yanıtladı - "(neredeyse) her şeyi keşfedilebilir hale getirmenin ana nedeni hata ayıklamaktı: hata ayıklamada genellikle soyutlamalardan kurtulmanız gerekiyor" - Yorum olarak ekledim çünkü çok geç - çok fazla cevap.
Peter M. - Monica

1
"Kasıtlı erişimi önle" ölçütünden geçerseniz, çoğu OOP dili gerçekte özel üyeleri desteklemez. Örneğin C ++ 'da belleğe ham erişiminiz vardır ve C #' da güvenilen kod özel yansımayı kullanabilir.
CodesInChaos

207

Özel işlev örneği

import re
import inspect

class MyClass :

    def __init__(self) :
        pass

    def private_function ( self ) :
        try :
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the begining
            matched = re.match( '^self\.', function_call )
            if not matched :
                print 'This is Private Function, Go Away'
                return
        except :
            print 'This is Private Function, Go Away'
            return

        # This is the real Function, only accessible inside class #
        print 'Hey, Welcome in to function'

    def public_function ( self ) :
        # i can call private function from inside the class
        self.private_function()

### End ###

12
self = MyClass() self.private_function(). : D Tabii ki sınıflarda çalışmıyor, ama sadece özel bir fonksiyon tanımlamanız gerekiyor:def foo(self): self.private_function()
Casey Kuball

161
Açıkça görülmüyorsa: bunu asla gerçek kodda yapmayın;)
Sudo Bash

10
@ThorSummoner Ya da sadece function_call.startswith('self.').
nyuszika7h

13
inspect.stack()[1][4][0].strip()<- Bu 1, 4 ve 0 sihirli sayıları nedir?
akhy

5
Bu yapılarak kolayca yenilebilir self = MyClass(); self.private_function()ve x = self.private_function()bir yöntemin içinde kullanıldığında başarısız olur .
Will

171

Java'dan Python'a ilk geldiğimde bundan nefret ettim . Beni ölümüne korkuttu.

Bugün Python ile ilgili en sevdiğim tek şey bu olabilir .

İnsanların birbirlerine güvendikleri ve kodları etrafında aşılmaz duvarlar inşa etmeleri gerektiğini hissetmedikleri bir platformda olmayı seviyorum. Güçlü bir şekilde kapsüllenmiş dillerde, bir API'nın bir hatası varsa ve neyin yanlış gittiğini anladıysanız, gerekli yöntem özel olduğu için hala bu sorunu çözemeyebilirsiniz. Python'da tutum “emin” dir. Durumu anladığınızı düşünüyorsanız, belki de okudunuz bile, söyleyebileceğimiz tek şey "iyi şanslar!" Dır.

Unutmayın, kapsülleme "güvenlik" ya da çocukları çimden uzak tutmakla bile ilgili değildir. Bir kod tabanının anlaşılmasını kolaylaştırmak için kullanılması gereken başka bir modeldir.


36
@CamJackson Javascript senin örneğidir ?? Prototip temelli mirasa ve işlevsel programlamayı destekleyen bir dile sahip tek yaygın dil mi? Bence geleneksel OOP'tan birkaç dikey adım attığı için JS'yi öğrenmek diğer dillerin çoğundan daha zor. Bu aptalların JS yazmasını engellemez, sadece bilmiyorlar;)
K.Steff

23
API'lar aslında kapsüllemenin neden önemli olduğuna ve özel yöntemlerin ne zaman tercih edileceğine gerçekten iyi bir örnektir. Özel olması amaçlanan bir yöntem, sonraki herhangi bir yeni sürümde kaybolabilir, imzayı değiştirebilir veya tüm değişiklik davranışlarının en kötüsü olabilir. Akıllı, yetişkin ekip üyeniz, güncellemenizden bir yıl sonra özel olarak tasarlanmış bir yönteme eriştiğini gerçekten hatırlayacak mı? Artık orada çalışacak mı?
einnocent

2
Bu tartışmaya katılmıyorum. Üretim kodunda, büyük olasılıkla hiçbir zaman "iş" yapmak için genel üyeleri değiştirmemi sağlayan bir hataya sahip bir API kullanmam. Bir API çalışmalıdır. Değilse, ben bir hata raporu dosyalamak veya aynı API kendim yapmak. Felsefeyi sevmiyorum ve Python'a çok düşkün değilim, sözdizimi daha küçük senaryolar yazmayı eğlenceli hale getiriyor ...
Yngve Sneen Lindal

4
Java'da Method.setAccessible ve Field.setAccessible vardır. Ayrıca korkutucu mu?
Tony

15
Java ve C ++ 'daki zorlama, Java'nın Python yaparken kullanıcıyı rahatsız etmesinden kaynaklanmaz. Çünkü derleyici ve / veya vm, bu bilgiyi biliyorsa, işiyle ilgili nasıl bir şeyle uğraşırken çeşitli varsayımlar yapabilir, örneğin C ++ sanal çağrılar yerine normal eski C çağrılarını kullanarak tüm bir dolaylı katmanı atlayabilir ve yüksek performanslı veya yüksek hassasiyetli malzemeler üzerinde çalışırken önemlidir. Python doğası gereği dinamizminden ödün vermeden bilgiyi gerçekten iyi kullanamaz. Her iki dil de farklı şeyleri hedefliyor, bu yüzden ikisi de "yanlış" değil
Shayne

144

Gönderen http://www.faqs.org/docs/diveintopython/fileinfo_private.html

Açıkçası, özel yöntemlere sınıflarının dışında erişilebilir, kolay erişilebilir değil. Python'da hiçbir şey gerçekten özel değildir; dahili olarak, özel yöntemlerin ve niteliklerin adları, verilen adlarıyla erişilemez görünmelerini sağlamak için anında karıştırılır ve yönetilmez. MP3FileInfo sınıfının __parse yöntemine _MP3FileInfo__parse adıyla erişebilirsiniz. Bunun ilginç olduğunu kabul edin, o zaman asla gerçek kodda asla yapmayacağınıza söz verin. Özel yöntemler bir sebepten dolayı özeldir, ancak Python'daki diğer birçok şey gibi, onların özelliği nihayetinde bir zorlama değil, bir konudur.


202
ya da Guido van Rossum'un söylediği gibi: "hepimiz yetişkiniz."

35
-1: bu sadece yanlış. Çift alt çizgiler asla ilk etapta özel olarak kullanılmaz. Aşağıdaki Alya'nın cevabı, ad yönetimi sözdiziminin gerçek niyetini anlatıyor. Gerçek sözleşme tek bir alt çizgidir.
nosklo

2
Sadece bir alt çizgi ile deneyin ve elde ettiğiniz sonucu göreceksiniz. @nosklo
Billal Begueradj

93

Yaygın olarak kullanılan cümle "hepimiz yetişkinleri burada kabul ediyoruz" ifadesidir. Tek bir alt çizgi (pozlama) veya çift alt çizgi (gizleme) ekleyerek, sınıfınızdaki kullanıcıya üyeye bir şekilde 'özel' olmasını istediğinizi söylersiniz. Ancak, zorlayıcı bir nedenleri olmadığı sürece (örn. Hata ayıklayıcılar, kod tamamlama) diğer herkese sorumlu davranmalarına ve buna saygı duymalarına güveniyorsunuz.

Gerçekten özel bir şeye sahip olmanız gerekiyorsa, bunu bir uzantıda uygulayabilirsiniz (örneğin, CPython için C'de). Bununla birlikte, çoğu durumda, Pythonic'i bir şeyler yapmanın yolunu öğrenirsiniz.


korunan bir değişkene erişmek için kullanmam gereken bir tür sarıcı protokolü var mı?
intuited

3
"Korunmuş" değişkenler "özel" olandan daha fazla değildir. Alt çizgiyle başlayan bir özniteliğe erişmek istiyorsanız, bunu yapabilirsiniz (ancak yazarın bunu reddettiğini unutmayın). Çift alt çizgiyle başlayan bir özniteliğe erişmeniz gerekiyorsa, adınızı kendiniz yönetebilirsiniz, ancak neredeyse kesinlikle bunu yapmak istemezsiniz.
Tony Meyer

33

Herhangi bir dilde (C ++ 'da işaretçi aritmetiği, .NET / Java'da Yansımalar) üyelerin özelliğinin etrafından dolanamazsınız.

Önemli olan, özel yöntemi yanlışlıkla çağırmayı denediğinizde bir hata almanızdır. Ama kendinizi ayağınızdan vurmak istiyorsanız, devam edin ve yapın.

Düzenleme: Eşyalarınızı OO-kapsülleme ile korumaya çalışmıyorsunuz, değil mi?


2
Bir şey değil. Sadece geliştiriciye 'özel' mülklere erişmenin kolay ve bence büyülü bir yol vermenin garip olduğunu belirtiyorum.
willurd

2
Evet, sadece konuyu göstermeye çalıştım. Özel yapmak derleyiciyi şikayet ederek "buna doğrudan erişmemelisin" der. Ama gerçekten gerçekten yapabilmek istiyor. Ancak evet, Python'da diğer dillerden daha kolaydır.
Maximilian

7
Java'da, şeyleri kapsülleme yoluyla güvenli hale getirebilirsiniz, ancak bu, akıllı olmanızı ve güvenilmeyen kodu bir SecurityManager'da çalıştırmanızı ve çok dikkatli olmanızı gerektirir. Oracle bile bazen yanlış anlıyor.
Antimon

12

class.__stuffAdlandırma kuralı programcı o erişime anlamına gelmez haber verir __stuffdışarıdan. Mangling adı, kazara herkesin bunu yapmasını pek mümkün kılmaz.

Doğru, hala bunun etrafında çalışabilirsiniz, diğer dillerde olduğundan daha da kolaydır (BTW de bunu yapmanıza izin verir), ancak kapsüllemeyi önemsiyorsa hiçbir Python programcısı bunu yapmaz.


12

Modül öznitelik adları tek bir alt çizgiyle (ör. _Foo) başladığında da benzer davranış oluşur.

Bu şekilde adlandırılan modül nitelikleri, from*yöntem kullanılırken içe aktarılan bir modüle kopyalanmaz , örn:

from bar import *

Ancak, bu bir dil kısıtlaması değil, bir sözleşmedir. Bunlar özel nitelikler değildir; herhangi bir ithalatçı tarafından referans verilebilir ve manipüle edilebilir. Bazıları bundan dolayı Python'un gerçek kapsülleme uygulayamayacağını iddia ediyor.


12

Bu dil tasarım seçeneklerinden sadece biri. Bir düzeyde haklılar. Bunu yaparlar, böylece yöntemi denemek ve çağırmak için yolunuzdan oldukça uzaklara gitmeniz gerekir ve gerçekten buna çok ihtiyacınız varsa, oldukça iyi bir nedeniniz olmalı!

Hata ayıklama kancaları ve test, elbette sorumlu bir şekilde kullanılan olası uygulamalar olarak akla gelir.


4

Python 3.4 ile bu davranış:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

https://docs.python.org/3/tutorial/classes.html#tut-private

Mangling kurallarının çoğunlukla kazaları önlemek için tasarlandığını unutmayın; özel kabul edilen bir değişkene erişmek veya değiştirmek hala mümkündür. Bu, hata ayıklayıcı gibi özel durumlarda bile yararlı olabilir.

Soru eski olsa bile, pasajımın yardımcı olabileceğini umuyorum.


2

Özel yöntemler ve niteliklerle ilgili en önemli endişe, geliştiricilere bunu sınıfın dışında çağırmamalarını söylemektir ve bu kapsüllemedir. kişi kapsülleme güvenliğini yanlış anlayabilir. söz konusu sözdizimini kasten bahsettiğiniz (feryat) gibi durumlarda, kapsülleme istemezsiniz.

obj._MyClass__myPrivateMethod()

C # 'dan göç ettim ve ilk başta benim için de tuhaftı ama bir süre sonra sadece Python kod tasarımcılarının OOP hakkında düşünme şeklinin farklı olduğu fikrine geldim.


1

Python'un 'özel' yöntemleri neden özel değil?

Anladığım kadarıyla özel olamazlar . Mahremiyet nasıl uygulanabilir?

Açık cevap "özel üyelere sadece üzerinden ulaşılabilir self", ancak bu işe yaramaz - selfPython'da özel değildir, bir işlevin ilk parametresi için yaygın olarak kullanılan bir addan başka bir şey değildir.

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.