Python'da __init__ Devralma ve Geçersiz Kılma


129

'Python'a Dalın'ı okuyordum ve sınıflarla ilgili bölümde şu örneği veriyor:

class FileInfo(UserDict):
    "store file metadata"
    def __init__(self, filename=None):
        UserDict.__init__(self)
        self["name"] = filename

Yazar daha sonra, __init__yöntemi geçersiz kılmak istiyorsanız, ebeveyni __init__doğru parametrelerle açıkça çağırmanız gerektiğini söyler .

  1. Ya bu FileInfosınıfın birden fazla ata sınıfı varsa?
    • Tüm üst sınıfların __init__yöntemlerini açıkça çağırmam gerekir mi?
  2. Ayrıca, bunu geçersiz kılmak istediğim başka bir yöntem için yapmam gerekir mi?

3
Aşırı Yüklemenin, Geçersiz Kılmadan ayrı bir kavram olduğunu unutmayın.
Dana the Sane

Yanıtlar:


158

Kitap, alt sınıf-üst sınıf çağrılarına göre biraz tarihli. Ayrıca, yerleşik sınıfların alt sınıflamasına göre biraz eski.

Bugünlerde şöyle görünüyor:

class FileInfo(dict):
    """store file metadata"""
    def __init__(self, filename=None):
        super(FileInfo, self).__init__()
        self["name"] = filename

Aşağıdakilere dikkat et:

  1. Biz doğrudan yerleşik sınıflara gibi alt sınıf dict, list, tuplevb

  2. superFonksiyon kolları bu sınıfın üst sınıfı izlenmesi ve uygun bir şekilde işlevlerini çağıran.


5
daha iyi bir kitap / öğretici aramalı mıyım?
liewl

2
Öyleyse çoklu miras durumunda, super () hepsini sizin adınıza takip ediyor mu?
Dana

dict .__ init __ (öz) aslında, ama bunda yanlış bir şey yok - süper (...) çağrısı sadece daha tutarlı bir sözdizimi sağlar. (Çoklu miras için nasıl çalıştığından emin değilim, sanırım sadece bir süper sınıf başlangıç bulabilir )
David Z

4
Super () işlevinin amacı, birden çok kalıtımı ele almasıdır. Dezavantajı ise, pratikte çoklu kalıtımın çok kolay bir şekilde kırılmasıdır (bakınız < fuhm.net/super-harmful ).
STH

2
Evet, birden çok kalıtım ve temel sınıfın yapıcı argümanlarını alması durumunda, genellikle kendinizi kurucuları manuel olarak çağırırken bulursunuz.
Torsten Marek

18

Kalıtım almanız gereken her sınıfta, alt sınıfın başlatılması üzerine başlatılması gereken her sınıfın bir döngüsünü çalıştırabilirsiniz ... kopyalanabilen bir örnek daha iyi anlaşılabilir ...

class Female_Grandparent:
    def __init__(self):
        self.grandma_name = 'Grandma'

class Male_Grandparent:
    def __init__(self):
        self.grandpa_name = 'Grandpa'

class Parent(Female_Grandparent, Male_Grandparent):
    def __init__(self):
        Female_Grandparent.__init__(self)
        Male_Grandparent.__init__(self)

        self.parent_name = 'Parent Class'

class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
#---------------------------------------------------------------------------------------#
        for cls in Parent.__bases__: # This block grabs the classes of the child
             cls.__init__(self)      # class (which is named 'Parent' in this case), 
                                     # and iterates through them, initiating each one.
                                     # The result is that each parent, of each child,
                                     # is automatically handled upon initiation of the 
                                     # dependent class. WOOT WOOT! :D
#---------------------------------------------------------------------------------------#



g = Female_Grandparent()
print g.grandma_name

p = Parent()
print p.grandma_name

child = Child()

print child.grandma_name

2
For döngüsü Child.__init__gerekli gibi görünmüyor . Örnekten çıkardığımda, çocuğum hala "Büyükanne" yazdırıyor. Büyükbaba init, Parentsınıf tarafından ele alınmıyor mu?
Adam

4
Sanırım büyükanne ve büyükbaba init'leri zaten Parent's init tarafından ele alındı, değil mi?
johk95

15

Gerçekten yok olması çağırmak için __init__temel sınıf (ler) yöntemlerini, ancak genellikle istediğiniz taban sınıfları çalışmalarına sınıflar yöntemlerden kalanı için gerekli olan bazı önemli başlatmalarını yapacak çünkü bunu yapmak için.

Diğer yöntemler için niyetinize bağlıdır. Temel sınıfların davranışına bir şey eklemek istiyorsanız, kendi kodunuza ek olarak temel sınıflar yöntemini çağırmak isteyeceksiniz. Davranışı temelden değiştirmek istiyorsanız, temel sınıfın yöntemini çağırmayabilir ve tüm işlevselliği doğrudan türetilmiş sınıfta uygulayamazsınız.


4
Teknik bütünlük için, threading.Thread gibi bazı sınıflar, ebeveynin init'ini çağırmaktan kaçınmaya çalışırsanız, devasa hatalar atacaktır .
David Berger

5
Tüm bu "üssün kurucusunu aramak zorunda değilsin" konuşmasının son derece rahatsız edici olduğunu düşünüyorum. Onu bildiğim hiçbir dilde aramanıza gerek yok. Üyeleri başlatmayarak hepsi aynı şekilde çuvallayacaktır (ya da olmayacaktır). Temel sınıfları başlatmamayı önermek pek çok yönden yanlıştır. Bir sınıfın şimdi başlatmaya ihtiyacı yoksa, gelecekte buna ihtiyacı olacaktır. Yapıcı, sınıf yapısı / dil yapısının arayüzünün bir parçasıdır ve doğru şekilde kullanılmalıdır. Doğru kullanımı, onu türetilmiş kurucunuzda bazen çağırmaktır. Öyleyse yap.
AndreasT

2
"Temel sınıfları başlatmamayı önermek pek çok yönden yanlıştır." Hiç kimse temel sınıfı başlatmamayı önermedi. Cevabı dikkatlice okuyun. Her şey niyetle ilgili. 1) Temel sınıf init mantığını olduğu gibi bırakmak istiyorsanız, türetilmiş sınıfınızda init yöntemini geçersiz kılmıyorsunuz. 2) init mantığını temel sınıftan genişletmek istiyorsanız, kendi init yönteminizi tanımlar ve ardından ondan temel sınıf 'init yöntemini çağırırsınız. 3) Temel sınıfın init mantığını değiştirmek istiyorsanız, temel sınıftan birini çağırmadan kendi init yönteminizi tanımlarsınız.
wombatonfire

4

FileInfo sınıfında birden fazla üst sınıf varsa, o zaman kesinlikle tüm __init __ () işlevlerini çağırmalısınız. Aynı şeyi bir yıkıcı olan __del __ () işlevi için de yapmalısınız.


2

Evet, __init__her veli sınıfını aramanız gerekir . Her iki ebeveynde de bulunan bir işlevi geçersiz kılarsanız, aynı şey işlevler için de geçerlidir.

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.