python3 中類的面向物件特性
類
封裝,繼承,多型,動態生成類
封裝
python類中的封裝特性是通過命名來實現的
- private 用雙下劃線開頭,表示變數或者函式只在當前類中可見
- protect 用單下劃線開頭,表示變數或者函式只在當前類以及其子類中可見
- public 不以下劃線開頭,表示變數或者函式可以在任意類中使用
繼承
-
繼承多個類
class myClass(cls1,cls2): pass
-
super機制
-
super() 函式用於呼叫下一個父類(超類)並返回該父類例項的方法。
-
直接用類名呼叫父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及到查詢順序(MRO)、重複呼叫(鑽石繼承)等種種問題。總之前人留下的經驗就是:保持一致性。要不全部用類名呼叫父類,要不就全部用 super,不要一半一半。
super(type[, object-or-type])type指的是子類,object-or-type一般是self
-
經典類和新式類的方法解析順序(MRO,Method Resolution Order)
-
經典類,類自身或者其 Python 2.x中預設都是經典類,只有顯式繼承了object才是新式類,但是在Python 3.x中按照如下寫法,仍然會被認為是新式類
class A: pass
-
新式類,類自身或者其父類繼承了object則為新式類。 Python 3.x中預設都是新式類,不必顯式的繼承object
class A(object): pass
-
經典類解析原則
從左至右的深度優先遍歷,但是如果遍歷中出現重複的類,保留第一個
舉例:
class G:attr = 1 class P1(G): pass class P2(G): attr = 2 class D(P1,P2): pass # attr的順序為[D,P1,G,P2,G] # 因為重複的類只保留第一個,所以最終的解析順序為[D,P1,G,P2] print(D.attr)
- 新式類解析原則
新式類和經典類一樣都是從左至右的深度優先遍歷,但是如果遍歷中出現重複的類,只保留最後一個
舉例:
class G1(object): attr = 1 class G2(object
- 新式類多重繼承容易出錯的位置
如果C繼承的兩個類P1和P2都繼承了相同的類,但是P1和P2的MRO順序不一樣的話,會報錯- 類的示例圖
(object) / \ G1 G2 P1(G1,G2) P2(G2,G1) \ / C(P1,P2)
- 例項
class G1(object): attr = 1 class G2(object): attr = 3 class P1(G1,G2): pass class P2(G2,1): attr = 2 class D(P1,P2): pass #P1的MRO順序為[P1,G1,G2] #P2的MRO順序為[P2,G2,G1] print(D.attr) # 輸出報錯
- 報錯資訊:
Traceback (most recent call last): File "mro.py", line 4, in <module> class P2(G2,1): attr = 2 TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-
-
__new__函式和__init__函式
- __new__函式是例項化類的時候呼叫的,為__init__函式提供類物件,也就是__init__函式的第一個引數,所以_new__函式必須返回類物件,通常可以用super(A,cls)._new(cls)獲取
- __init__函式是初始化物件的時候呼叫的,其中的self引數即是__new__函式返回的。
多型
-
類具有繼承關係,並且子類型別可以向上轉型看做父類型別,如果父類中有這種方法,只要傳入相應引數就可以使用。
-
開閉原則
- 對擴充套件開放(Open for extension):允許子類重寫方法函式
- 對修改封閉(Closed for modification):不重寫,直接繼承父類方法函式
-
__call__函式
- 作用是把類的例項當作函式進行呼叫
- 舉例
class Fib(object): def __call__(self,num): return num f = Fib() # 此處f是類的例項 print f(10) # 此處把類的例項當成函式在用,實際上進行處理的就是__call__函式
動態生成類
-
類物件的型別,python中一切都是物件,類也是物件,如果是類,則為type。如果是物件,其型別的型別也還是type
>>> class MyCls(object):pass ... >>> MyCls.__class__ <class 'type'> >>> a=379 >>> a.__class__ <class 'int'> >>> a.__class__.__class__ <class 'type'> >>> a.__class__.__class__.__class__ <class 'type'>
-
type 函式,動態生成類
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
舉例
>>> class pClass(object):pass ... >>> child=type('ChildClass',(pClass,),{}) >>> child.__name__ 'ChildClass' >>> child.__class__ <class 'type'>
-
使用metaclass,生成動態類
- metaclass 作用
- metaclass的呼叫順序
類Foo中有metaclass這個屬性嗎?如果是,Python會在記憶體中通過metaclass建立一個名字為Foo的***類物件***(我說的是類物件)。如果沒有找到metaclass,會在(父類)中繼續尋找metaclass屬性,並嘗試做和前面同樣的操作。如果還是找不到metaclass,Python就會用內建的type來建立這個類物件。
- metaclass 使用方法
- 首先自定義metaclass,繼承type。
- 在自定義元類中的__new__函式中返回自定義類,也就是type(class_name,class_parents,class_attr)。
- 在想使用元類的類定義中設定metaclass屬性為目標元類。
- metaclass例項
class myMetaClass(type): def __new__(metaname, class_name, class_parents, class_attr): print("Hello hello") return super(myMetaClass,metaname).__new__(metaname, class_name, class_parents, class_attr) class Foo(object,metaclass=myMetaClass): pass f = Foo() print(type(Foo))
輸出
Hello hello <class '__main__.myMetaClass'>
擴充套件題
- ORM是如何實現的(程式碼在連線,by 廖雪峰)
- 描述器
- 動態生成類