sergiojune 平常學python
剛開始學習python的時候或者其餘的是面向對象的編程語言的時候,不免會對類和對象理解得不太清楚。因此今天和你們分享下python中的類和對象,深刻理解下python中的類和對象。python
當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就能夠被稱爲鴨子。這個就是鴨子類型的定義,在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
類變量就是在類內定義的,可是不在方法內定義的,並且前綴無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,這個很容易知道,用類來引用改變時markdown
Student.conutry = 'cn' # 這個是用類引用來進行修改
修改後打印下三個結果都是修改後的結果。可是下面這個呢?編程語言
s1.conutry = 'zhongguo' # 用實例來引用進行修改
此次結果就不同了,只有s1的類變量變了,其餘兩個都是不變的。這是爲何呢?就如上面所說,用實例引用來修改類變量的時候並非修改,而是新建了這個變量。又因爲python查找變量是由下往上查找的,因此會先查找出新建後的變量。ide
類屬性就是定義在類中的方法和變量,實例屬性也是同樣的。訪問順序就是由下往上查找的,用代碼體會一下。函數
class A(): name = 'A' def __init__(self): self.name = 'a' a = A() print(a.name) # 結果 a
因爲是類變量先加載,再到初始化對象,因此纔會運行init()方法,因此結果很顯然就是a。這裏因爲該類沒有繼承,就沒有很複雜,可是當出現多繼承,幾個類之間就變得很複雜,這個時候的訪問順序就難多了。下面說下這兩種狀況,掌握了這兩種狀況,其餘的基本沒有問題了。學習
(1.適合深度優先查找
A繼承了B,C,B,C分別繼承了D,E。深度優先的查找是先去着A,若是A中沒有該屬性,就去B着,再沒有就去D找。D中找不到了再去C找。這種查找狀況是沒有問題的,可是另外一種狀況就不合適了。code
2)適合廣度優先查找
這個是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(B, C): 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(B, C): pass print(A.__mro__) # 結果 (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
這個時候就用了廣度優先算法,符合與咱們剛纔所說的,這就是C3算法了哈。
學過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
顯然是錯誤,結果是這個
是否是以爲很奇怪,可是又很熟悉?是的,這個也是按照剛纔的查找順序同樣執行的,若是不信的話咱們打印下mro就知道了
是否是恰好倒敘?由於咱們是先打印父類的再打印本身的,因此順序倒了。再看看另一種狀況也是可行的
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(D, C): 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:若是以爲文章不錯的話,歡迎隨手點贊轉發支持