2011-06-18 202 views
13

似乎沒有好的在線文檔: 如果我創建派生類,它是否會自動擁有基類的所有屬性?但是什麼是BaseClass.__init(),你是否也需要對其他基類方法做? BaseClass.__init__()需要參數嗎?如果你有基類__init__()的參數,它們是否也被派生類使用,你是否需要明確地將參數設置爲派生類的參數__init__(),或將它們設置爲BaseClass.__init__()而不是?Python派生類和基類的屬性?

回答

9

If I make a derived class, will it automatically have all the attributes of the base class?

類屬性,是的。實例屬性,no(僅僅因爲它們在創建類時不存在),除非在派生類中沒有__init__,在這種情況下,將調用基本類,並設置實例屬性。

Does BaseClass.init() need arguments?

取決於類和它的__init__簽名。如果您明確地在派生類中調用Base.__init__,則至少需要通過self作爲第一個參數。如果你有

class Base(object): 
    def __init__(self): 
     # something 

那麼很明顯,__init__沒有其他參數被接受。如果你有

class Base(object): 
    def __init__(self, argument): 
     # something 

,那麼你必須調用基__init__時候該傳球argument。這裏沒有火箭科學。

If you have arguments for your base class init(), are they also used by the derived class, do you need to explicitly set the arguments to the derived classe's init(), or set them to BaseClass.init() instead?

再次,如果派生類沒有__init__,基地一個將被用來代替。

class Base(object): 
    def __init__(self, foo): 
     print 'Base' 

class Derived(Base): 
    pass 

Derived() # TypeError 
Derived(42) # prints Base 

在其他情況下,您需要以某種方式照顧它。無論您使用*args, **kwargs,只是將未修改的參數傳遞給基類,或者複製基類簽名,或從其他地方提供參數,都取決於您想要完成的工作。

43

如果在派生自BaseClass的類中實現__init__,則它將覆蓋繼承的方法,因此BaseClass.__init__將永遠不會被調用。如果您需要爲BaseClass調用__init__方法(通常情況下),那麼您可以直接調用BaseClass.__init__,通常從新實現的__init__方法中完成。

class Foo(object): 
    def __init__(self): 
     self.a = 10 

    def do_something(self): 
     print self.a 

class Bar(Foo): 
    def __init__(self): 
     self.b = 20 

bar = Bar() 
bar.do_something() 

這將導致以下錯誤:

AttributeError: 'Bar' object has no attribute 'a' 

所以,do_something方法一直延續下來的預期,但這種方法需要屬性a已經設置,這不可能是因爲__init__也被覆蓋。我們通過在Bar.__init__之內明確地呼叫Foo.__init__來解決這個問題。

class Foo(object): 
    def __init__(self): 
     self.a = 10 

    def do_something(self): 
     print self.a 

class Bar(Foo): 
    def __init__(self): 
     Foo.__init__(self) 
     self.b = 20 

bar = Bar() 
bar.do_something() 

按預期打印10。在這種情況下,Foo.__init__需要一個參數Foo(按照慣例稱爲self)。

通常,當您在類的實例上調用方法時,類實例會自動作爲第一個參數傳遞。一個類的實例的方法被稱爲綁定方法bar.do_something是一個綁定方法的例子(你會注意到它沒有任何參數被調用)。 Foo.__init__是一個未綁定方法,因爲它未附加到Foo的特定實例,因此需要明確傳遞第一個參數,即Foo的實例。

在我們的例子中,我們通過對selfFoo.__init__,這是傳遞給__init__方法BarBar實例。由於Bar繼承自Foo,所以Bar的實例也是Foo的實例,因此允許將self傳遞給Foo.__init__

很有可能是您繼承的類需要或接受的參數多於類的實例。這將打印20

class Foo(object): 
    def __init__(self, a=10): 
     self.a = a 

    def do_something(self): 
     print self.a 

class Bar(Foo): 
    def __init__(self): 
     Foo.__init__(self, 20) 

bar = Bar() 
bar.do_something() 

:這些,你會與你從__init__內調用的任何方法處理。

如果您試圖通過繼承類來實現完全公開基類的所有初始化參數的接口,則需要明確地執行此操作。這通常是通過* args和** kwargs參數完成的(名稱是按照慣例),它們是所有未明確命名的其餘參數的佔位符。下面的示例使用的一切我已經討論:

class Foo(object): 
    def __init__(self, a, b=10): 
     self.num = a * b 

    def do_something(self): 
     print self.num 

class Bar(Foo): 
    def __init__(self, c=20, *args, **kwargs): 
     Foo.__init__(self, *args, **kwargs) 
     self.c = c 

    def do_something(self): 
     Foo.do_something(self) 
     print self.c 


bar = Bar(40, a=15) 
bar.do_something() 

在這種情況下,參數c設置爲40,因爲它是第一個參數Bar.__init__。然後將第二個參數合併到變量argskwargs中(*和**是特定的語法,表示在傳遞給函數/方法時將列表/元組或字典擴展爲單獨的參數),並傳遞給Foo.__init__

這個例子也使得任何覆蓋方法需要顯式調用,如果這是需要什麼(如do_something在這種情況下)的地步。

最後一點,您將經常看到super(ChildClass, self).method()(其中ChildClass是某些任意子類),而不是顯式調用BaseClass方法。關於super的討論是另外一個問題,但足以說明,在這些情況下,通常通過調用BaseClass.method(self)來完成正在完成的工作。簡而言之,super以方法解析順序將方法調用委託給下一個類 - MRO(在單一繼承中是父類)。有關更多信息,請參見documentation on super