Pyhton-類(2)

 

·類(2)

 

@ 繼承(inheritance)


什麼是繼承:編程

B繼承A:A是父類(超類),B是子類(基類)。繼承能夠實現代碼重複利用,實現屬性和方法繼承。函數

繼承可使子類擁有父類的屬性和方法,也能夠從新定義某些屬性、重寫某些方法,即覆蓋父類原有的屬性和方法,使其得到父類不一樣的功能。固然,也能夠在子類中新設置屬性和方法。從技術上看,OOP裏繼承最主要的用途是實現多態,對於多態而言,最重要的是接口的繼承性(屬性和方法是否存在繼承性),這是不必定的。繼承也不是全爲了代碼的重複利用,而是爲了理順關係。post

對於 Python 中的繼承,前面一直在使用,那就是咱們寫的類都是新式類,全部新式類都是繼承自 object 類。不要忘記,新式類的一種寫法:spa

class NewStyle(object):
    pass

這就是典型的繼承。code

繼承的基本概念:orm

class Person:
    def __init__(self,name):
        self.name = name
        
    def get_name(self):
        print ("hello,{}!".format(self.name))

    def setHeight(self, n):
        self.length = n

    def breast(self, n):
        print ("My breast is: ",n)

class Boy(Person):
    def setHeight(self):
        print ("The height is:1.80m .")


j = Boy('jimmy')
j.get_name()
j.setHeight()
j.breast(90)
打印結果: hello,jimmy! The height is:1.80m .
My breast is:  90

首先,定義了一個父類 Person,定義一個子類 Boy,Boy類的括號中是Person,這就意味着 Boy 繼承了 Person,Boy 是 Person 的子類,Person 是 Boy 的父類。那麼 Boy 就所有擁有了 Person 中的方法和屬性。blog

若是 Boy 裏面有一個和 Person 一樣名稱的方法,那麼就把 Person 中的同一個方法遮蓋住了,顯示的是 Boy 中的方法,這叫作方法的重寫實例化類 Boy以後,執行實例方法  j.setHeight(),因爲在類 Boy中重寫了 setHeight 方法,那麼 Person 中的那個方法就不顯做用了,在這個實例方法中執行的是類 Boy 中的方法。繼承

雖然在類 Boy 中沒有看到 get_name() 方法,可是由於它繼承了 Person,因此 j.get_name() 就執行類 Person 中的方法。同理  j.breast(90) ,它們就好像是在類 Boy 裏面已經寫了這兩個方法同樣。既然繼承了,就可使用。接口

多重繼承:get

子類繼承多個父類:

 

class Person:
    def eye(self):
        print("two eyss")
    def breast(self,n):
        print("the breast is:",n)
class Girl:
    age = 18
    def color(self):
        print("the girl is white.")
class HotGirl(Person,Girl):
    pass

 

在類的名字後面的括號中把所繼承的兩個類的名字寫上,HotGirl 就繼承了Person 和 Girl這兩個類。實例化類 HotGirl,既然繼承了上面的兩個類,那麼那兩個類的方法就都可以拿過來使用。在類 Girl 中, age = 28,在對 HotGirl 實例化以後,由於繼承的緣由,這個類屬性也被繼承到 HotGirl 中,所以經過實例獲得它。

繼承的特色,即將父類的方法和屬性所有承接到子類中;若是子類重寫了父類的方法,就使用子類的該方法,父類的被遮蓋。

 多重繼承的順序:

若是一個子類繼承了兩個父類,而且兩個父類有一樣的方法或者屬性,那麼在實例化子類後,調用那個方法或屬性,是屬於哪一個父類的呢?造一個沒有實際意義,純粹爲了解決這個問題的程序:

class K1(object):
    def foo(self):
        print("K1-foo")
class K2(object):
    def foo(self):
        print("K2-foo")
    def bar(self):
        print("K2-bar")
class J1(K1, K2):
    pass
class J2(K1, K2):
    def bar(self):
        print("J2-bar")
class C(J1, J2):
    pass

print(C.__mro__)
m = C()
m.foo()
m.bar()
打印結果: (<class '__main__.C'>, <class '__main__.J1'>, <class '__main__.J2'>, <class '__main__.K1'>, <class '__main__.K2'>, <class 'object'>)
K1-foo
J2-bar

print C.__mro__打印出類的繼承順序。

從上面清晰看出來了,若是要執行 foo() 方法,首先看 J1,沒有,看 J2,尚未,看 J1 裏面的 K1,有了,即 C(沒有)==>J1(沒有)==>J2(沒有)==>K1(找到了)bar() 也是按照這個順序,在 J2 中就找到了一個。

這種對繼承屬性和方法搜索的順序稱之爲廣度優先新式類用以及 Python3.x 中都是按照此順序原則搜尋屬性和方法的。

可是,在舊式類中,是按深度優先的順序的。由於後面讀者也基本不用舊式類,因此不舉例。

 

@ super函數


 對於初始化函數的繼承,跟通常方法的繼承不一樣:

class Person:
    def __init__(self):
        self.height = 160
    def about(self, name):
        print("{} is about {}".format(name, self.height))
class Girl(Person):
    def __init__(self):
        self.breast = 90
    def about(self, name):
        print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast))

cang = Girl()
cang.about("wangguniang")
打印結果:
Traceback (most recent call last):
  File "test1.py", line 14, in <module>
    cang.about("wangguniang")
  File "test1.py", line 11, in about
    print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast))
AttributeError: 'Girl' object has no attribute 'height'

在上面這段程序中,類 Girl 繼承了類 Person。在類 Girl 中,初始化設置了 self.breast = 90,因爲繼承了 Person,按照前面的經驗,Person 的初始化函數中的 self.height = 160 也應該被 Girl 所繼承過來。而後在重寫的 about 方法中,就是用 self.height

實例化類 Girl,並執行 cang.about("wangguniang"),試圖打印出一句話 wangguniang is a hot girl, she is about 160, and her bereast is 90。保存程序,運行報錯!

信息顯示 self.height 是不存在的。也就是說類 Girl 沒有從 Person 中繼承過來這個屬性。

 Girl中發現, about 方法重寫了,__init__方法,也被重寫了。它跟類 Person 中的__init__重名了,也一樣是重寫了那個初始化函數。這是由於在子類中重寫了某個方法以後,父類中一樣的方法被遮蓋了。

使用super 函數:

class Person:
    def __init__(self):
        self.height = 160
    def about(self, name):
        print("{} is about {}".format(name, self.height))
class Girl(Person):
    def __init__(self):
        super().__init__()
        self.breast = 90
    def about(self, name):
        print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast))

cang = Girl()
cang.about("wangguniang")
打印結果: wangguniang is a hot girl, she is about 160, and her breast is 90

在子類中,__init__方法重寫了,爲了調用父類同方法,使用 super(Girl, self).__init__()的方式。super 函數的參數,第一個是當前子類的類名字,第二個是 self,而後是點號,點號後面是所要調用的父類的方法。一樣在子類重寫的 about 方法中,也能夠調用父類的 about 方法。

最後要提醒注意:super 函數僅僅適用於新式類。

 

@ 綁定方法與非綁定方法


要經過實例來調用類的方法(函數),常常要將類實例化。方法是類內部定義函數,只不過這個函數的第一個參數是 self。(能夠認爲方法是類屬性,但不是實例屬性)。必須將類實例化以後,才能經過實例調用該類的方法。調用的時候在方法後面要跟括號(括號中默認有 self 參數,能夠不寫出來)。經過實例調用方法,稱這個方法綁定在實例上。

 調用綁定方法:
class Person(object):
    def foo(self):
        pass
pp = Person()    #實例化
print(pp.foo())  #調用方法

這樣就實現了方法和實例的綁定,因而經過 pp.foo() 便可調用該方法。

調用非綁定方法:

class Person:
    def __init__(self):
        self.height = 160
    def about(self, name):
        print("{} is about {}".format(name, self.height))
class Girl(Person):
    def __init__(self):
        super().__init__()
        self.breast = 90
    def about(self, name):
        print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast))

cang = Girl()
cang.about("wangguniang")
打印結果: wangguniang is a hot girl, she is about 160, and her breast is 90

在子類 Girl 中,由於重寫了父類的__init__方法,若是要調用父類該方法,在上節中不得不使用 super().__init__()調用父類中由於子類方法重寫而被遮蔽的同名方法。

其實,非綁定方法由於在子類中,沒有創建父類的實例,卻要是用父類的方法。對於這種非綁定方法的調用,還有一種方式。不過這種方式如今已經較少是用了,由於有了 super 函數。爲了方便讀者看其它有關代碼,仍是要簡要說明。

例如在上面代碼中,在類 Girl 中想調用父類 Person 的初始化函數,則須要在子類中,寫上這麼一行:Person.__init__(self)

這不是經過實例調用的,而是經過類 Person 實現了對__init__(self)的調用。這就是調用非綁定方法的用途。可是,這種方法已經被 super 函數取代,因此,若是在編程中遇到相似狀況,推薦使用 super 函數。

 

@ 靜態方法和類方法


已知,類的方法第一個參數必須是 self,而且若是要調用類的方法,必須將經過類的實例,即方法綁定實例後才能由實例調用。若是不綁定,通常在繼承關係的類之間,能夠用 super 函數等方法調用。

這裏再介紹一種方法,這種方法的調用方式跟上述的都不一樣,這就是:靜態方法類方法

class StaticMethod:
    @staticmethod
    def foo():
        print("This is static method foo().")

class ClassMethod:
    @classmethod
    def bar(cls):              #類方法中要有cls參數
        print("This is class method bar().")
        print("bar() is part of class:", cls.__name__)

static_foo = StaticMethod()    #實例化
static_foo.foo()               #實例調用靜態方法
StaticMethod.foo()             #經過類來調用靜態方法
print("********")
class_bar = ClassMethod()
class_bar.bar()
ClassMethod.bar()
打印結果: This is static method foo().
This is static method foo().
********
This is class method bar().
bar() is part of class: ClassMethod
This is class method bar().
bar() is part of class: ClassMethod

Python 中:

@staticmethod表示靜態方法

@classmethod表示類方法

先看靜態方法,雖然名爲靜態方法,但也是方法,因此,依然用 def 語句來定義。須要注意的是文件名後面的括號內,沒有self,這和前面定義的類中的方法是不一樣的,也正是因着這個不一樣,纔給它另外取了一個名字叫作靜態方法。若是沒有 self,那麼也就沒法訪問實例變量、類和實例的屬性了,由於它們都是藉助 self 來傳遞數據的。

在看類方法,一樣也具備通常方法的特色,區別也在參數上。類方法的參數也沒有 self,可是必須有 cls 這個參數在類方法中,可以訪問類屬性,可是不能訪問實例屬性

簡要明確兩種方法。下面看調用方法。兩種方法均可以經過實例調用,即綁定實例也能夠經過類來調用,即 StaticMethod.foo() 這樣的形式,這也是區別通常方法的地方,通常方法必須用經過綁定實例調用。

 

end~
 
****** 幾米花的Python ****** 博客主頁:https://www.cnblogs.com/jimmy-share/  歡迎轉載 ~
相關文章
相關標籤/搜索