1. 程式人生 > >【python】詳解類class的繼承、__init__初始化、super方法(五)

【python】詳解類class的繼承、__init__初始化、super方法(五)

通過之前四篇的介紹:

  • 【python】python中的類,物件,方法,屬性初認識(一)詳見連結
  • 【python】詳解類class的屬性:類資料屬性、例項資料屬性、特殊的類屬性、屬性隱藏(二)詳見連結
  • 【python】詳解類class的方法:例項方法、類方法、靜態方法(三)詳見連結
  • 【python】詳解類class的訪問控制:單下劃線與雙下劃線_(四)詳見連結

Python中類相關的一些基本點已經比較完整清晰了,本文繼續深入Python中類的繼承和_ _slots _ _屬性。

1、繼承

  • 在Python中,同時支援單繼承與多繼承,一般語法如下:
class SubClassName(ParentClass1 [, ParentClass2, ...
]): class_suite
  • 實現繼承之後,子類將繼承父類的屬性,也可以使用內建函式insubclass()來判斷一個類是不是另一個類的子孫類
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 21 22:33:09 2018

@author: BruceWong
"""

class Parent(object):
    '''
    parent class
    '''
    numList = []
    def numdiff(self, a, b):
        return a-b

class Child
(Parent):
pass c = Child() # subclass will inherit attributes from parent class #子類繼承父類的屬性 Child.numList.extend(range(10)) print(Child.numList) print("77 - 2 =", c.numdiff(77, 2)) # built-in function issubclass() print(issubclass(Child, Parent)) print(issubclass(Child, object)) # __bases__ can show all the parent classes
#bases屬性檢視父類 print('the bases are:',Child.__bases__) # doc string will not be inherited #doc屬性不會被繼承 print(Parent.__doc__) print(Child.__doc__)

程式碼的輸出為:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
77 - 2 = 75
True
True
the bases are: (<class '__main__.Parent'>,)

    parent class

None

例子中唯一特別的地方是文件字串。文件字串對於類,函式/方法,以及模組來說是唯一的,也就是說doc屬性是不能從父類中繼承來的。

2、繼承中的_ _init_ _
當在Python中出現繼承的情況時,一定要注意初始化函式_init_的行為:

  • 如果子類沒有定義自己的初始化函式,父類的初始化函式會被預設呼叫;但是如果要例項化子類的物件,則只能傳入父類的初始化函式對應的引數,否則會出錯。
  • 如果子類定義了自己的初始化函式,而在子類中沒有顯示呼叫父類的初始化函式,則父類的屬性不會被初始化
  • 如果子類定義了自己的初始化函式,在子類中顯示呼叫父類,子類和父類的屬性都會被初始化

2.1、子類沒有定義自己的初始化函式,父類的初始化函式會被預設呼叫:

#定義父類:Parent
class Parent(object):
    def __init__(self, name):
        self.name = name
        print("create an instance of:", self.__class__.__name__)
        print("name attribute is:", self.name)
#定義子類Child ,繼承父類Parent       
class Child(Parent):
    pass
#子類例項化時,由於子類沒有初始化,此時父類的初始化函式就會預設被呼叫
#且必須傳入父類的引數name
c = Child("init Child") 

子類例項化時,由於子類沒有初始化,此時父類的初始化函式就會預設被呼叫,此時傳入父類的引數name,輸出結果為:

create an instance of: Child
name attribute is: init Child

如果不傳入父類的引數name:

class Parent(object):
    def __init__(self, name):
        self.name = name
        print("create an instance of:", self.__class__.__name__)
        print("name attribute is:", self.name)

class Child(Parent):
    pass

#c = Child("init Child") 
#print()    
c = Child()

沒有傳入父類name引數的輸出結果會報錯:

Traceback (most recent call last):

  File "<ipython-input-11-9a7781a6f192>", line 1, in <module>
    runfile('C:/Users/BruceWong/.spyder-py3/類的繼承.py', wdir='C:/Users/BruceWong/.spyder-py3')

  File "C:\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 866, in runfile
    execfile(filename, namespace)

  File "C:\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 102, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/Users/BruceWong/.spyder-py3/類的繼承.py", line 54, in <module>
    c = Child()

TypeError: __init__() missing 1 required positional argument: 'name'

2.2、子類定義了自己的初始化函式,而在子類中沒有顯示呼叫父類的初始化函式,則父類的屬性不會被初始化

class Parent(object):
    def __init__(self, name):
        self.name = name
        print("create an instance of:", self.__class__.__name__)
        print("name attribute is:", self.name)
#子類繼承父類        
class Child(Parent):
    #子類中沒有顯示呼叫父類的初始化函式
    def __init__(self):
        print("call __init__ from Child class")
#c = Child("init Child") 
#print()  
#將子類例項化  
c = Child()
print(c.name)

在子類中沒有顯示呼叫父類的初始化函式,則父類的屬性不會被初始化,因而此時呼叫子類中name屬性不存在:
AttributeError: ‘Child’ object has no attribute ‘name’

call __init__ from Child class
Traceback (most recent call last):

  File "<ipython-input-12-9a7781a6f192>", line 1, in <module>
    runfile('C:/Users/BruceWong/.spyder-py3/類的繼承.py', wdir='C:/Users/BruceWong/.spyder-py3')

  File "C:\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 866, in runfile
    execfile(filename, namespace)

  File "C:\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 102, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/Users/BruceWong/.spyder-py3/類的繼承.py", line 56, in <module>
    print(c.name)

AttributeError: 'Child' object has no attribute 'name'

2.3、如果子類定義了自己的初始化函式,顯示呼叫父類,子類和父類的屬性都會被初始化

class Parent(object):
    def __init__(self, name):
        self.name = name
        print("create an instance of:", self.__class__.__name__)
        print("name attribute is:", self.name)

class Child(Parent):
    def __init__(self):
        print("call __init__ from Child class")
        super(Child,self).__init__("data from Child")   #要將子類Child和self傳遞進去
#c = Child("init Child") 
#print() 
d = Parent('tom')   
c = Child()
print(c.name)

子類定義了自己的初始化函式,顯示呼叫父類,子類和父類的屬性都會被初始化的輸出結果:

#例項化父類Parent的結果
create an instance of: Parent
name attribute is: tom

#例項化子類Child的結果
call __init__ from Child class
#super首先會先使得父類初始化的引數進行例項化
create an instance of: Child
name attribute is: data from Child
data from Child

3、super的使用詳解

  • super主要來呼叫父類方法來顯示呼叫父類,在子類中,一般會定義與父類相同的屬性(資料屬性,方法),從而來實現子類特有的行為。也就是說,子類會繼承父類的所有的屬性和方法,子類也可以覆蓋父類同名的屬性和方法
class Parent(object):
    Value = "Hi, Parent value"
    def fun(self):
        print("This is from Parent")
#定義子類,繼承父類               
class Child(Parent):
    Value = "Hi, Child  value"
    def ffun(self):
        print("This is from Child")

c = Child()    
c.fun()
c.ffun()
print(Child.Value)

輸出結果:

This is from Parent
This is from Child
Hi, Child value

但是,有時候可能需要在子類中訪問父類的一些屬性,可以通過父類名直接訪問父類的屬性,當呼叫父類的方法是,需要將”self”顯示的傳遞進去的方式

class Parent(object):
    Value = "Hi, Parent value"
    def fun(self):
        print("This is from Parent")

class Child(Parent):
    Value = "Hi, Child  value"
    def fun(self):
        print("This is from Child")
        Parent.fun(self)   #呼叫父類Parent的fun函式方法

c = Child()    
c.fun()

輸出結果:

This is from Child
This is from Parent  #例項化子類Child的fun函式時,首先會列印上條的語句,再次呼叫父類的fun函式方法

這種方式有一個不好的地方就是,需要經父類名硬編碼到子類中,為了解決這個問題,可以使用Python中的super關鍵字:

class Parent(object):
    Value = "Hi, Parent value"
    def fun(self):
        print("This is from Parent")

class Child(Parent):
    Value = "Hi, Child  value"
    def fun(self):
        print("This is from Child")
        #Parent.fun(self)
        super(Child,self).fun()  #相當於用super的方法與上一呼叫父類的語句置換

c = Child()    
c.fun()

輸出結果:

This is from Child
This is from Parent  #例項化子類Child的fun函式時,首先會列印上條的語句,再次呼叫父類的fun函式方法