深入理解python中的類和物件

sergiojune發表於2019-03-02

剛開始學習python的時候或者其他物件導向的程式語言的時候,難免會對類和物件理解得不太清楚。所以今天和大家分享下python中的類和物件,深入理解下python中的類和物件。

1.鴨子型別

當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。這個就是鴨子型別的定義,在python中,並不關心這個物件是什麼型別,只關心他的行為。由行為來推斷出該物件所屬於的型別。就比如列表(list)、元組(tuple)、字典(dict)等等,這些類都是可迭代的,所以說他們是可迭代物件。

from collections import Iterable
l = [1, ]
t = (1, )
d = {`d`3}
print(isinstance(l, Iterable))
print(isinstance(t, Iterable))
print(isinstance(d, Iterable))

# 結果
True
True
True複製程式碼

2.類變數和例項變數

類變數就是在類內定義的,但是不在方法內定義的,而且字首無self作為引用。例項變數就是有self作為引用的存在類中的變數。類變數是所有物件共享的,在類中修改時,其他的物件也會跟著變。但是需要注意的是,如果是用物件來引用類變數進行修改的話,這裡只是新建了和類變數同名的例項變數,並沒有修改到。下面用程式碼解釋下。

class Student(object):
   conutry = `China`  # 這個是類變數

   def __init__(self, name, sex):
       self.name = name  # 這個是例項變數,也就是物件變數
       self.sex = sex  # 物件變數


s1 = Student(`張三``man`)
s2 = Student(`里斯``woman`)
print(s1.conutry)
print(s2.conutry)
print(Student.conutry)複製程式碼

上面的結果都是三個China,這個很容易知道,用類來引用改變時

Student.conutry = `cn`  # 這個是用類引用來進行修改
複製程式碼

修改後列印下三個結果都是修改後的結果。但是下面這個呢?

s1.conutry = `zhongguo`  # 用例項來引用進行修改

這次結果就不一樣了,只有s1的類變數變了,其他兩個都是不變的。這是為什麼呢?就如上面所說,用例項引用來修改類變數的時候並不是修改,而是新建了這個變數。又由於python查詢變數是由下往上查詢的,所以會先查詢出新建後的變數。

3.類屬性和例項屬性之間的訪問順序

類屬性就是定義在類中的方法和變數,例項屬性也是一樣的。訪問順序就是由下往上查詢的,用程式碼體會一下。

class A():
   name = `A`
   def __init__(self):
       self.name = `a`

a = A()
print(a.name)
# 結果
a複製程式碼

由於是類變數先載入,再到初始化物件,所以才會執行__init__()方法,所以結果很顯然就是a。這裡由於該類沒有繼承,就沒有很複雜,但是當出現多繼承,幾個類之間就變得很複雜,這個時候的訪問順序就難多了。下面說下這兩種情況,掌握了這兩種情況,其他的基本沒有問題了。

(1.適合深度優先查詢

深入理解python中的類和物件

A繼承了B,C,B,C分別繼承了D,E。深度優先的查詢是先去著A,如果A中沒有該屬性,就去B著,再沒有就去D找。D中找不到了再去C找。這種查詢情況是沒有問題的,但是另一種情況就不合適了。

2)適合廣度優先查詢

深入理解python中的類和物件

這個是A繼承了B,C,B,C都繼承了D。如果這個用深度優先的演算法的話,就會出現一個問題,因為深度優先查詢順序是A->B->D->C。這個是不太合理的,當C中過載了D中的一個方法後,B沒有過載,如果要查詢C中的該方法,用深度優先的演算法就只能找到D中的原始方法,所以說這就不正確了,這時候就需要用廣度優先的 演算法,這個時候查詢順序就是A->B->C->D。但是當遇到上面的情況時又會出錯了。這時怎麼辦?python3就將所有的屬性搜尋演算法統一成了一個演算法:C3演算法,這裡就不展開說這個演算法了,因為太複雜了:)會根據對應情況而實施對應演算法,下面用程式碼來分別體會下以上兩種情況

class E():
   pass

class D():
   pass

class C(E):
   pass

class B(D):
   pass

class A(BC):
   pass

print(A.__mro__)
# 結果
(<class `__main__.A`>, <class `__main__.B`>, <class `__main__.D`>, <class `__main__.C`>, <class `__main__.E`>, <class `object`>)複製程式碼

__mro__這個屬性是獲取屬性的查詢順序,可以看到就是和我們上面說的一樣,用了深度優先的演算法。再看另一個

class D():
   pass

class C(D):
   pass

class B(D):
   pass

class A(BC):
   pass

print(A.__mro__)
# 結果
(<class `__main__.A`>, <class `__main__.B`>, <class `__main__.C`>, <class `__main__.D`>, <class `object`>)複製程式碼

這個時候就用了廣度優先演算法,符合與我們剛才所說的,這就是C3演算法了哈。

4.super真的是呼叫父類嗎?

學過Java的都知道,super()這個方法就是在呼叫父類的方法,但是在python中就不一定了。我們先看看super的用法

class A():
   def __init__(self):
       print(`A`)

class B(A):
   def __init__(self):
       # python2做法是
       # super(A, self).__init__()
       super().__init__()  # 呼叫父類的初始化方法
       print(`B`)

b = B()
# 結果
A
B複製程式碼

上面就是用法了,python2和python3用法不一樣,這裡我們就只用python3了就行操作。接下來看看super真正的呼叫情況。

class A():
   def __init__(self):
       print(`A`)

class B(A):
   def __init__(self):
       super().__init__()
       print(`B`)

class C(A):
   def __init__(self):
       super().__init__()
       print(`C`)

class D(B, C):
   def __init__(self):
       super().__init__()
       print(`D`)

d = D()複製程式碼

上面這個是我們之前所說的那個適合廣度優先演算法的多繼承,按照我們之前的理解,super呼叫的是父函式,那麼這個結果就會是:

          A   B   C   D

顯然是錯誤,結果是這個

深入理解python中的類和物件

是不是覺得很奇怪,但是又很熟悉?是的,這個也是按照剛才的查詢順序一樣執行的,如果不信的話我們列印下__mro__就知道了

深入理解python中的類和物件

是不是剛好倒敘?因為我們是先列印父類的再列印自己的,所以順序倒了。再看看另外一種情況也是可行的

class A():
   def __init__(self):
       print(`A`)

class B():
   def __init__(self):
       super().__init__()
       print(`B`)

class C(A):
   def __init__(self):
       super().__init__()
       print(`C`)

class D(B):
   def __init__(self):
       super().__init__()
       print(`D`)

class E(DC):
   def __init__(self):
       super().__init__()
       print(`E`)

e = E()
print(E.__mro__)
# 結果
A
C
B
D
E
(<class `__main__.E`>, <class `__main__.D`>, <class `__main__.B`>, <class `__main__.C`>, <class `__main__.A`>, <class `object`>)複製程式碼

也是和預期一樣的。總的來說,super不一定是呼叫父類,它的呼叫順序也是遵循mro演算法的,就是屬性查詢演算法,和上文說的C3演算法一致。

有任何問題歡迎在留言區提問,或者有不當的地方也歡迎指出

ps:如果覺得文章不錯的話,歡迎隨手點贊轉發支援

日常學python

程式碼不止bug,還有美和樂趣

深入理解python中的類和物件

相關文章