__İnit __ () ana sınıfın __init __ () 'i çağırmalı mı?


132

Ben Objective-C'de şu yapıyı kullanıyorum:

- (void)init {
    if (self = [super init]) {
        // init class
    }
    return self;
}

Python ayrıca üst sınıfın uygulamasını da çağırmalı __init__mı?

class NewClass(SomeOtherClass):
    def __init__(self):
        SomeOtherClass.__init__(self)
        # init class

Bu da __new__()ve için doğru / yanlış __del__()mı?

Düzenleme: Çok benzer bir soru var: Python'da Devralma ve Geçersiz Kılma__init__


Kodunuzu önemli ölçüde değiştirdiniz. Orijinalin objectbir yazım hatası olduğunu anlayabiliyorum . Ama şimdi supersorunun atıfta bulunduğu başlığa bile sahip değilsin .
SilentGhost

Sadece süper'in ebeveyn sınıfı için bir isim olarak kullanıldığını düşündüm. Kimsenin işlevi düşüneceğini düşünmemiştim. Herhangi bir yanlış anlaşılma için özür dilerim.
Georg Schölly

Yanıtlar:


67

Python'da süper sınıfı çağırmak __init__isteğe bağlıdır. Onu çağırırsanız, supertanımlayıcının kullanılıp kullanılmayacağı veya süper sınıfı açıkça adlandırılıp adlandırılmayacağı da isteğe bağlıdır :

object.__init__(self)

Nesne durumunda, süper yöntemi boş olduğu için süper yöntemi çağırmak kesinlikle gerekli değildir. Aynı __del__.

Öte yandan, __new__gerçekten de süper yöntemi çağırmalı ve yeni oluşturulan nesne olarak dönüşünü kullanmalısınız - açıkça farklı bir şey döndürmek istemediğiniz sürece.


Öyleyse, sadece yöneticinin uygulamasını aramak için bir sözleşme yok mu?
Georg Schölly

5
Eski tarz sınıflarda, süper init'i yalnızca süper sınıfın tanımlanmış bir initi varsa (ki bu genellikle olmaz) olabilirdi. Bu nedenle, insanlar tipik olarak onu prensip dışı yapmak yerine süper yöntemi çağırmayı düşünürler.
Martin - Löwis

1
Python'daki sözdizimi kadar basit [super init]olsaydı, daha yaygın olurdu. Sadece spekülatif bir düşünce; Python 2.x'teki süper yapı bana biraz garip geliyor.
u0b34a0f6ae

Burada ilginç (ve muhtemelen çelişkili) bir örnek gibi görünüyor: bytes.com/topic/python/answers/… init
mlvljr

"isteğe bağlı" diye çağırmak zorunda değilsiniz, ancak onu çağırmazsanız, otomatik olarak aranmayacaktır.
McKay

140

__init__Mevcut sınıfta yapılmakta olanlara ek olarak üstlerden bir şeyin yapılmasına ihtiyacınız varsa __init__,, bunu kendiniz aramanız gerekir, çünkü bu otomatik olarak gerçekleşmeyecektir. Ama süper bir şeye ihtiyacınız yoksa __init__,onu aramanıza gerek yok. Misal:

>>> class C(object):
        def __init__(self):
            self.b = 1


>>> class D(C):
        def __init__(self):
            super().__init__() # in Python 2 use super(D, self).__init__()
            self.a = 1


>>> class E(C):
        def __init__(self):
            self.a = 1


>>> d = D()
>>> d.a
1
>>> d.b  # This works because of the call to super's init
1
>>> e = E()
>>> e.a
1
>>> e.b  # This is going to fail since nothing in E initializes b...
Traceback (most recent call last):
  File "<pyshell#70>", line 1, in <module>
    e.b  # This is going to fail since nothing in E initializes b...
AttributeError: 'E' object has no attribute 'b'

__del__aynı şekildedir (ancak __del__sonuçlandırmaya güvenme konusunda dikkatli olun - bunun yerine with ifadesi aracılığıyla yapmayı düşünün).

Nadiren kullanıyorum __new__. , tüm başlatmayı şurada yapıyorum:__init__.


3
D (C) sınıfının tanımı bu şekilde düzeltilmelidirsuper(D,self).__init__()
eyquem

11
super () .__ init __ () yalnızca Python 3'te çalışır. Python 2'de super (D, self) .__ init __ ()
Jacinda

"Süper init'ten bir şeye ihtiyacınız varsa ..." - Bu çok sorunlu bir ifadedir, çünkü bu sizin / alt sınıfın "bir şeye" ihtiyacı olup olmadığı değil, temel sınıfın geçerli olmak için bir şeye ihtiyaç duyup duymadığı ile ilgilidir. temel sınıf örneği ve düzgün çalışır. Türetilmiş sınıfın uygulayıcısı olarak, temel sınıf iç öğeleri bilmeyeceğiniz / bilmemeniz gereken şeylerdir ve hem yazdığınız için hem de iç kısımlar belgelendiği için yapsanız bile, temelin tasarımı gelecekte değişebilir ve kötü yazılmış bir durum nedeniyle bozulabilir. Türetilmiş sınıf. Bu nedenle daima temel sınıfın tam olarak başlatıldığından emin olun.
Nick

105

Anon'un cevabında:
" __init__Mevcut sınıfta yapılanlara ek olarak süper bir şeyin yapılmasına ihtiyacınız varsa __init__, bunu kendiniz aramalısınız, çünkü bu otomatik olarak olmayacak"

İnanılmaz: Miras ilkesinin tam tersini ifade ediyor.


O değil "süper en dan bir şey __init__ (...) otomatik olmayacak" taban sınıfı çünkü otomatik gerçekleşmesi İSTİYORUM yani ama olmaz __init__türetilmiş CLAS tanımı ile değiştirileceğini__init__

Öyleyse, NEDEN türetilmiş_sınıfı tanımlamak, birisi mirasa başvurduğunda amaçlanan __init__şeyi geçersiz kılar?

Bunun nedeni, kişinin temel sınıfta YAPILMAYAN bir şeyi tanımlaması gerektiğidir __init__ve bunu elde etmenin tek yolu, yürütülmesini türetilmiş bir sınıf __init__işlevine koymaktır .
Başka bir deyişle, eğer bu ikincisi geçersiz kılınmamışsa, 'temel sınıfta__init__ otomatik olarak yapılacak şeye ek olarak ' temel sınıfta bir şeye ihtiyaç vardır__init__ .
Aksine DEĞİL.


O halde sorun, temel sınıfta mevcut olan istenen talimatların __init__artık somutlaştırma anında etkinleştirilmemesidir. Bu inaktivasyon dengelemek amacıyla, özel bir şey gereklidir: açıkça taban sınıf çağırarak __init__, amacıyla TUTUN , ADD İÇİN DEĞİL, başlatma baz sınıfı tarafından gerçekleştirilen __init__. Resmi belgede tam olarak söylenen budur:

Türetilmiş bir sınıftaki geçersiz kılma yöntemi, aslında aynı ada sahip temel sınıf yöntemini değiştirmek yerine genişletmek isteyebilir . Temel sınıf yöntemini doğrudan çağırmanın basit bir yolu vardır: sadece BaseClassName.methodname (self, argümanlar) çağırın.
http://docs.python.org/tutorial/classes.html#inheritance

Tüm hikaye bu:

  • amaç, temel sınıf tarafından gerçekleştirilen başlatmayı, yani saf kalıtımı TUTMAK olduğunda, özel bir şey gerekmez, yalnızca __init__türetilmiş sınıfta bir işlevi tanımlamaktan kaçınılmalıdır.

  • amaç, temel sınıf tarafından gerçekleştirilen başlatma işlemini DEĞİŞTİRMEK olduğunda, __init__türetilmiş sınıfta tanımlanmalıdır.

  • Amaç, temel sınıf tarafından gerçekleştirilen başlatmaya işlemleri EKLEMEK olduğunda, temel sınıfa __init__ yönelik açık bir çağrı içeren bir türetilmiş sınıf tanımlanmalıdır.__init__


Anon'un yazısında hayret verici hissettiğim şey, sadece miras teorisinin tersini ifade etmesi değil, aynı zamanda kılını kıpırdatmadan oy alan 5 kişinin geçtiği ve ayrıca 2 yıl içinde tepki verecek kimsenin olmadığıdır. ilginç konusu nispeten sık okunması gereken bir konu.


1
Bu gönderinin olumlu oylanacağından emindim. Korkarım nedenleri hakkında çok fazla açıklamam olmayacak. Olumsuz oy vermek, anlaşılmaz görünen bir metni analiz etmekten daha kolaydır. Anon'un gönderisini anlamaya çalışırken uzun zaman geçirdim, sonunda onun özel bir şekilde yazıldığını ve pek de otoriter olmadığını fark ettim. Belki de mirası bilen biri için yaklaşık olarak doğru olarak yorumlanabilir; ancak kalıtım hakkında kararsız düşüncelere sahip biri tarafından okuduğumda kafa karıştırıcı buluyorum, bu konu genel olarak kaya suyu kadar net değil
eyquem

2
"Bu gönderinin yeniden oylanacağından emindim ..." Ana sorun, partiye biraz geç kalmanız ve çoğu insanın ilk birkaç cevabın ötesini okumaması olabilir. Bu arada harika açıklama +1
Gerrat

Harika cevap bu.
Trilarion

3
Aaron'dan alıntıladığın cümlede "ek" kelimesini kaçırdın. Aaron'un ifadesi tamamen doğrudur ve söylediklerinize uymaktadır.
GreenAsJade

1
Bu, python'un tasarım seçimini mantıklı kılan ilk açıklamadır.
Joseph Garvin

20

Düzenleme : (kod değişikliğinden sonra)
Ebeveyninizi __init__(veya başka herhangi bir işlevi) aramanız gerekip gerekmediğini size söylememizin bir yolu yoktur . Kalıtım açıkça böyle bir çağrı olmadan işe yarayacaktır. Her şey kodunuzun mantığına bağlıdır: örneğin, her __init__şeyiniz ebeveyn sınıfında yapılırsa, alt sınıfı __init__tamamen atlayabilirsiniz .

aşağıdaki örneği düşünün:

>>> class A:
    def __init__(self, val):
        self.a = val


>>> class B(A):
    pass

>>> class C(A):
    def __init__(self, val):
        A.__init__(self, val)
        self.a += val


>>> A(4).a
4
>>> B(5).a
5
>>> C(6).a
12

Süper çağrıyı örneğimden kaldırdım, birinin ebeveyn sınıfın init uygulamasını çağırıp çağırmayacağını bilmek istiyorum .
Georg Schölly

o zaman başlığı düzenlemek isteyebilirsiniz. ama cevabım hala geçerli.
SilentGhost

5

Zor ve hızlı bir kural yok. Bir sınıfa ilişkin dokümantasyon, alt sınıfların üst sınıf yöntemini çağırıp çağırmayacağını belirtmelidir. Bazen süper sınıf davranışını tamamen değiştirmek ve diğer zamanlarda onu artırmak istersiniz - yani, bir üst sınıf çağrısından önce ve / veya sonra kendi kodunuzu çağırın.

Güncelleme: Aynı temel mantık, herhangi bir yöntem çağrısı için geçerlidir. Yapıcılar bazen özel bir değerlendirmeye (çoğu zaman davranışı belirleyen bir durum oluşturdukları için) ve yıkıcılara ihtiyaç duyarlar çünkü paralel kurucular (örneğin kaynakların tahsisinde, örneğin veritabanı bağlantıları). Ancak aynısı, örneğin render()bir widget yöntemi için de geçerli olabilir .

Daha fazla güncelleme: OPP nedir? OOP mi demek istiyorsun? Hayır - bir alt sınıfın genellikle süper sınıfın tasarımı hakkında bir şeyler bilmesi gerekir . Dahili uygulama ayrıntıları değil - ancak üst sınıfın müşterileriyle yaptığı temel sözleşme (sınıfları kullanarak). Bu, OOP ilkelerini hiçbir şekilde ihlal etmez. Bu nedenle protectedgenel olarak OOP'de geçerli bir kavramdır (elbette Python'da olmasa da).


Bazen birinin süper sınıf çağrısından önce kendi kodunu çağırmak isteyeceğini söyledin. Bunu yapmak için, OPP'yi ihlal edecek olan ebeveyn sınıfının uygulaması hakkında bilgiye ihtiyaç vardır.
Georg Schölly

4

IMO, onu aramalısın. Eğer üst sınıfınız objectöyleyse, yapmamalısınız, ancak diğer durumlarda onu çağırmamak olağanüstü bir durumdur. Başkaları tarafından zaten yanıtlandığı gibi, sınıfınızın __init__kendi kendini geçersiz kılması gerekmiyorsa , örneğin başlatılacak (ek) iç durumu olmadığında çok kullanışlıdır .


2

Evet, temel sınıfı her zaman __init__açıkça iyi bir kodlama uygulaması olarak çağırmalısınız . Bunu yapmayı unutmak, ince sorunlara veya çalışma zamanı hatalarına neden olabilir. Bu, __init__herhangi bir parametre almasa bile doğrudur . Bu, derleyicinin sizin için örtük olarak temel sınıf yapıcısını çağırdığı diğer dillerden farklıdır. Python bunu yapmaz!

Her zaman temel sınıfı çağırmanın ana nedeni _init__, temel sınıfın tipik olarak üye değişkeni yaratması ve bunları varsayılanlara başlatabilmesidir. Dolayısıyla, temel sınıf initini çağırmazsanız, bu kodların hiçbiri çalıştırılmaz ve sonunda üye değişkenleri olmayan temel sınıf elde edersiniz.

Örnek :

class Base:
  def __init__(self):
    print('base init')

class Derived1(Base):
  def __init__(self):
    print('derived1 init')

class Derived2(Base):
  def __init__(self):
    super(Derived2, self).__init__()
    print('derived2 init')

print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()

Bu yazdırır ..

Creating Derived1...
derived1 init
Creating Derived2...
base init
derived2 init

Bu kodu çalıştırın .

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.