Pyhton-類(2)

jimmy0k發表於2019-01-06

 

·類(2)

 

@ 繼承(inheritance)


什麼是繼承:

B繼承A:A是父類(超類),B是子類(基類)。繼承可以實現程式碼重複利用,實現屬性和方法繼承。

繼承可以使子類擁有父類的屬性和方法,也可以重新定義某些屬性、重寫某些方法,即覆蓋父類原有的屬性和方法,使其獲得父類不同的功能。當然,也可以在子類中新設定屬性和方法。從技術上看,OOP裡繼承最主要的用途是實現多型,對於多型而言,最重要的是介面的繼承性(屬性和方法是否存在繼承性),這是不一定的。繼承也不是全為了程式碼的重複利用,而是為了理順關係。

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

class NewStyle(object):
    pass

這就是典型的繼承。

繼承的基本概念:

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 中的方法和屬性。

如果 Boy 裡面有一個和 Person 同樣名稱的方法,那麼就把 Person 中的同一個方法遮蓋住了,顯示的是 Boy 中的方法,這叫做方法的重寫例項化類 Boy之後,執行例項方法  j.setHeight(),由於在類 Boy中重寫了 setHeight 方法,那麼 Person 中的那個方法就不顯作用了,在這個例項方法中執行的是類 Boy 中的方法。

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

多重繼承:

子類繼承多個父類:

 

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/  歡迎轉載 ~

相關文章