Neden açıkça bir Python yönteminde “self” argümanına ihtiyacınız var?


197

Python'daki bir sınıfta bir yöntem tanımlarken, şuna benzer:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

Ancak C # gibi diğer bazı dillerde, yöntemin "this" anahtar sözcüğüyle bağlandığı nesneye, yöntem prototipinde argüman olarak bildirilmeden başvurunuz olur.

Bu Python'da kasıtlı bir dil tasarımı kararı mıydı, yoksa “benliğin” argüman olarak kabul edilmesini gerektiren bazı uygulama detayları var mı?


15
Bahse girerim neden selfüyelere erişmek için neden açıkça yazmanız gerektiğini bilmek istersiniz - stackoverflow.com/questions/910020/…
Piotr Dobrogost

1
Ama bir kazan tabağı gibi görünüyor
Raghuveer

Biraz kafa karıştırıcı ama anlaşılmaya değer stackoverflow.com/a/31367197/1815624
CrandellWS

Yanıtlar:


91

Python'dan Peters 'Zen'i alıntılamayı seviyorum. "Açık, örtük olmaktan iyidir."

Java ve C ++ this.'da, çıkarılmasını imkansız kılan değişken isimleriniz dışında ' 'çıkarılabilir. Yani bazen ihtiyacınız olur, bazen de istemezsiniz.

Python böyle bir şeyi bir kurala dayanmak yerine açık yapmayı seçer.

Ayrıca, hiçbir şey ima edilmediğinden veya varsayılmadığından, uygulamanın bazı bölümleri ortaya çıkar. self.__class__, self.__dict__Ve diğer "iç" yapılar bariz bir şekilde kullanılabilir.


53
Unutmayın daha az şifreli bir hata mesajına sahip olmak güzel olurdu.
Martin Beckett

9
Ancak, bir yöntemi çağırdığınızda nesne değişkenini iletmeniz gerekmez, açıklık kuralını ihlal etmez mi? Bu zen'i tutmak için, şöyle bir şey olmalı: object.method (object, param1, param2). Bir şekilde tutarsız görünüyor ...
Vedmant

10
"açık, örtük olmaktan daha iyidir" - Python'un "stili" örtük olan şeyler etrafında inşa edilmemiş midir? ör. örtük veri türleri, örtük işlev sınırları ({} yok), örtük değişken kapsam ... modüllerde genel değişkenler işlevlerde mevcutsa ... neden sınıflara aynı mantık / akıl yürütme uygulanmamalıdır? Basitleştirilmiş kural sadece girinti ile belirlendiği üzere "daha yüksek bir seviyede beyan edilen herhangi bir şey daha düşük bir seviyede kullanılabilir" olmaz mı?
Simon

13
Saçma saptandı
Vahid Amiri

10
yüzleşelim, sadece kötü. Bunun hiçbir bahanesi yok. Sadece çirkin bir kalıntı ama sorun yok.
Toskan

63

Yöntemler ve fonksiyonlar arasındaki farkı en aza indirmektir. Metasınıflarda kolayca yöntem oluşturmanıza veya önceden var olan sınıflara çalışma zamanında yöntemler eklemenize olanak tanır.

Örneğin

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Ayrıca (bildiğim kadarıyla) python çalışma zamanının uygulanmasını kolaylaştırır.


10
+1 for Metotlar ve fonksiyonlar arasındaki farkı en aza indirmektir. Bu cevap kabul edilmelidir
kullanıcı

Bu da guido'nun çok bağlantılı açıklamasının kökeninde.
Marcin

1
Bu aynı zamanda Python'da c.bar () işlevini ilk yaptığınızda örneği öznitelikler için denetlediğini, daha sonra sınıf özniteliklerini kontrol ettiğini gösterir . Böylece, bir Veriyi veya işlevi (nesneleri) bir Sınıfa istediğiniz zaman 'ekleyebilir' ve örneğinde erişmeyi bekleyebilirsiniz (yani dir (örnek) nasıl olur). Sadece c örneğini "oluşturduğunuzda" değil. Çok dinamik.
Nishant

10
Gerçekten satın almıyorum. Üst sınıfa ihtiyaç duyduğunuz durumlarda bile, yürütme sırasında yine de çıkarımda bulunabilirsiniz. Örnek metotları ile sınıf fonksiyonları arasında geçen örneklerin denkliği saçmadır; Ruby onlarsız iyi gidiyor.
Mart'ta zachaysan

2
JavaScript, bir nesneye çalışma zamanında yöntemler eklemenize izin verir selfve işlev bildiriminde gerektirmez (JavaScript'in oldukça zor thisbağlayıcı semantiği olduğu için belki de bu bir cam evden taş atıyor )
Jonathan Benn

55

Guido van Rossum'un bu konudaki blogunu okumasını öneriyorum - Neden açık benlik kalmak zorunda .

Bir yöntem tanımı dekore edildiğinde, otomatik olarak bir 'self' parametresi verilip verilmeyeceğini bilmiyoruz: dekoratör işlevi statik bir yönteme ('self' içermeyen) veya bir sınıf yöntemine ( bir örnek yerine bir sınıfa gönderme yapan komik bir tür kendine sahiptir) veya tamamen farklı bir şey yapabilir (saf Python'da '@classmethod' veya '@staticmethod' uygulayan bir dekoratör yazmak önemsizdir). Dekoratörün, metodun örtülü bir 'benlik' argümanı ile donatılıp donatılmamasına izin verip vermeyeceğini bilmeden bir yol yoktur.

Özel kasa '@classmethod' ve '@staticmethod' gibi saldırıları reddediyorum.


16

Python sizi "kendini" kullanmaya zorlamaz. İstediğiniz adı verebilirsiniz. Bir yöntem tanımı başlığındaki ilk argümanın nesneye bir başvuru olduğunu hatırlamanız gerekir.



1
Kendini her yöntemde ilk param olarak koymaya zorlar, sadece benim için pek mantıklı olmayan fazladan bir metin. Diğer diller bununla iyi çalışır.
Vedmant

haklı mıyım her zaman ilk parametre nesneye bir referanstır.
Mohammad Mehdi KouchakYazdi

@MMKY Hayır, örneğin @staticmethoddeğil.
Mark

1
"Sadece bir yöntem tanımındaki ilk argümanı hatırlamak zorunda ..." Ben "öz" kelimesini "kwyjibo" olarak değiştirmeyi denedim ve hala çalıştı. Sıklıkla açıklandığı gibi, bu önemli olan "benlik" kelimesi değil, o alanı kaplayan şeyin konumu (?)
RBV

7

Ayrıca bunu yapmanıza izin verir: (kısaca, çağırmak Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)12 geri döner, ancak bunu en çılgın yollarla yapar.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Tabii ki, bu Java ve C # gibi dillerde hayal etmek daha zordur. Öz referansı açık hale getirerek, herhangi bir nesneye söz konusu referansla başvurabilirsiniz. Ayrıca, çalışma zamanında sınıflarla böyle bir oyun oynamanın daha statik dillerde yapılması daha zordur - bu mutlaka iyi veya kötü değildir. Sadece açık benlik tüm bu deliliğin var olmasına izin verir.

Dahası, şunu hayal edin: Yöntemlerin davranışını özelleştirmek istiyoruz (profilleme veya çılgın kara büyü için). Bu bizi düşünmemize yol açabilir: Methodya davranışını geçersiz kılabileceğimiz ya da kontrol edebileceğimiz bir sınıfımız olsaydı ?

İşte burada:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

Ve şimdi: InnocentClass().magic_method()beklendiği gibi davranacak. Yöntem , MagicMethod örneğine ve innocent_selfparametresine bağlı olacaktır . Tuhaf ha? 2 anahtar kelimeye sahip olmak ve Java ve C # gibi dillerde kullanmak gibi. Bunun gibi sihir, çerçevelerin aksi halde çok daha ayrıntılı olacak şeyler yapmasına izin verir.InnocentClassmagic_selfthis1this2

Yine, bu tür şeyler hakkında yorum yapmak istemiyorum. Sadece açık bir öz referans olmadan yapılması zor olan şeyleri göstermek istedim.


4
İlk örneğinizi düşündüğümde, Java'da da aynısını yapabilirim: iç sınıf OuterClass.this, dış sınıftan 'benliği' almak için çağırmalıdır , ancak yine thisde kendisine bir referans olarak kullanabilirsiniz ; Python'da yaptıklarınıza çok benzer. Benim için bunu hayal etmek zor değildi. Belki kişinin söz konusu dilde yeterliliğine bağlıdır?
klaar

Ancak, anonim bir sınıf içinde tanımlanan, anonim bir arabirim Somethingiçinde tanımlanan ve daha sonra başka bir anonim uygulamanın içinde tanımlanan bir anonim sınıf yönteminin içindeyken herhangi bir kapsama başvurabilir misiniz Something? Python'da elbette kapsamlardan herhangi birine başvurabilirsiniz.
vlad-ardelean

Haklısınız, Java'da dış sınıfa yalnızca açık sınıf adını çağırabilir ve bunu önek olarak kullanabilirsiniz this. Örtük başvurular Java'da imkansızdır.
klaar

Bu olsa işe yarayacak mı acaba: Her kapsamda (her yöntem) thissonucu referans yerel bir değişken var . Örneğin Object self1 = this;(Nesne kullanın veya daha az genel bir şey kullanın). Daha yüksek kapsamları değişken erişiminiz varsa Ardından, erişim için olabilir self1, self2... selfn. Bunların nihai olarak ilan edilmesi gerektiğini düşünüyorum, ama işe yarayabilir.
vlad-ardelean

3

Bence "Python Zen" i asıl nedeni Fonksiyonların Python birinci sınıf vatandaşlar olmasıdır.

Bu onları aslında bir Nesne yapar. Şimdi temel sorun, işlevleriniz de nesne ise, Nesne yönelimli paradigmada, mesajların kendileri nesne olduğunda Nesnelere nasıl mesaj gönderirsiniz?

Bir tavuk yumurtası problemine benziyor, bu paradoksu azaltmak için, tek olası yöntem ya bir yürütme bağlamını yöntemlere aktarmak veya tespit etmektir. Ancak, pitonun iç içe geçmiş işlevleri olabileceğinden, yürütme bağlamı iç işlevler için değişeceğinden bunu yapmak imkansız olacaktır.

Bu, olası tek çözümün açıkça “ben” i (yürütme bağlamı) geçmek olduğu anlamına gelir.

Bu yüzden Zen'in daha sonra geldiği bir uygulama sorunu olduğuna inanıyorum.


Merhaba ben python (java arka plandan) yeniyim ve "mesajların kendileri nesne olduğunda nasıl Nesnelere mesaj göndermek istiyorsunuz?" Bu neden bir sorun, ayrıntılandırabilir misin?
Qiulang

1
@Qiulang Aah, Nesne yönelimli programlamada nesneler üzerinde çağrı yöntemleri, yükü olan veya olmayan Nesnelere (işlevinize parametreler) mesaj göndermeye eşdeğerdir. Yöntemler dahili olarak bir sınıf / nesne ile ilişkilendirilmiş bir kod bloğu olarak temsil edilir ve çağrıldığı nesne aracılığıyla kendisine sağlanan kapalı ortamı kullanır. Ancak yöntemleriniz nesne ise, bu yöntemi hangi ortama karşı çalıştırırsanız çağırırsanız soruyu başlatan bir sınıf / nesne ile ilişkili olmaktan bağımsız olarak var olabilirler mi?
pankajdoharey

Dolayısıyla, bir ortam sağlamak için bir mekanizma olmalı, benlik icra noktasında mevcut ortam anlamına gelecektir, ancak başka bir ortam da sağlayabilirsiniz.
pankajdoharey

2

Bence PEP 227 ile ilgisi var:

Sınıf kapsamındaki adlara erişilemez. İsimler, en içteki kapatma işlevi kapsamında çözümlenir. İç içe kapsamlar zincirinde bir sınıf tanımı oluşursa, çözümleme işlemi sınıf tanımlarını atlar. Bu kural, sınıf öznitelikleri ile yerel değişken erişimi arasındaki garip etkileşimleri önler. Sınıf tanımında bir ad bağlama işlemi gerçekleşirse, sonuçta elde edilen sınıf nesnesinde bir öznitelik oluşturur. Bu değişkene bir yöntemde veya bir yöntemle iç içe yerleştirilmiş bir işlevde erişmek için, öznitelik başvurusunun kendinden veya sınıf adıyla kullanılması gerekir.


1

Python'da kendi içinde açıklandığı gibi , Demystified

obj.meth (args) gibi herhangi bir şey Class.meth (obj, args) olur. Arama işlemi, alma işlemi (açık) değilken otomatiktir. Bu, sınıftaki bir işlevin ilk parametresinin nesnenin kendisi olması gerektiğidir.

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

invocations:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () üç parametreyi tanımlar, ancak iki tanesini (6 ve 8) geçtik. Benzer şekilde distance () için bir tane gerekir ancak sıfır bağımsız değişken iletilir.

Python neden bu argüman numarası uyuşmazlığından şikayet etmiyor ?

Genel olarak, bazı argümanlarla bir yöntem çağırdığımızda, yöntemin nesnesi ilk argümanın önüne yerleştirilerek karşılık gelen sınıf işlevi çağrılır. Böylece, obj.meth (args) gibi bir şey Class.meth (obj, args) olur. Arama işlemi, alma işlemi (açık) değilken otomatiktir.

Sınıftaki bir işlevin ilk parametresinin nesnenin kendisi olmasının nedeni budur. Bu parametreyi kendilik olarak yazmak sadece bir kuraldır . Bir anahtar kelime değildir ve Python'da özel bir anlamı yoktur. Başka isimler de kullanabiliriz (bunun gibi) ama kesinlikle söylememenizi öneririm. Kendinden başka isimler kullanmak çoğu geliştirici tarafından kaşlarını çatar ve kodun okunabilirliğini azaltır ("Okunabilirlik önemlidir").
...
İçinde, ilk örnek self.x bir örnek özniteliği iken x yerel bir değişkendir. Aynı değiller ve farklı ad alanlarında yatıyorlar.

Ben Kalmak İçin Burada

Birçoğu kendini C ++ ve Java'da olduğu gibi Python'da bir anahtar kelime yapmayı önerdi . Bu, yöntemlerde biçimsel parametre listesinden açık benliğin gereksiz kullanımını ortadan kaldıracaktır. Bu fikir umut verici görünse de, olmayacak. En azından yakın gelecekte değil. Ana nedeni geriye dönük uyumluluktur . İşte Python'un yaratıcısından, açık benliğin neden kalması gerektiğini açıklayan bir blog.


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.