使用MethodType函數將方法綁定到類或實例上
阿新 • • 發佈:2017-07-01
變量 python object 靜態 code 輸出結果 ssm 指向 復制
在開始正文之前,需要了解下Python的綁定方法(bound method)和非綁定方法。
簡單做個測試:
定義一個類,類中由實例方法、靜態方法和類方法。
class ClassA: def instance_method(self): print(‘instance_method‘, self) @classmethod def cls_method(cls): print(‘cls_method‘, cls) @staticmethod def static_method(): print(‘static_method‘)
逐個測試,測試的結果在註釋說說明。
class_a = ClassA() print(‘測試實例方法、靜態方法、類方法與實例和類的關系‘) # 類中的實例方法,與類本身並沒有綁定關系 # <function ClassA.instance_method at 0x0000022744592488> print(ClassA.instance_method) # 類中的靜態方法,與類也沒有綁定關系 # <function ClassA.static_method at 0x0000027A5F6D0598> print(ClassA.static_method)# 而類中的類方法,是和這個類存在綁定關系的 # <bound method ClassA.cls_method of <class ‘__main__.ClassA‘>> print(ClassA.cls_method) print(‘-‘ * 50) # 實例中的實例方法,與實例存在綁定關系 # 因為當通過一個實例去訪問類中的某方法時,會形成綁定關系,將實例作為第一個參數self傳入。 # <bound method ClassA.instance_method of <__main__.ClassA object at 0x000001B117D07710>>print(class_a.instance_method) # 類方法與實例也存在綁定關系,所以實例可以直接調用類方法 # <bound method ClassA.cls_method of <class ‘__main__.ClassA‘>> print(class_a.cls_method) # 靜態方法與實例沒有綁定關系 # <function ClassA.static_method at 0x0000027D36340620> print(class_a.static_method)
接著嘗試把一個函數,綁定到類或者實例上。
第一種方法,直接將函數賦值給類。
# 創建實例 class_a class_a = ClassA() # 直接給類屬性賦值 ClassA.func_a = func_a # 輸出結果,沒有綁定關系 # <function func_a at 0x10e41ff28> print(ClassA.func_a) # 通過實例訪問之前賦值的類屬性 # 對於賦值之前創建得實例,因為是通過實例訪問,所以也會存在綁定關系 # <bound method func_a of <__main__.ClassA object at 0x10e4330b8>> print(class_a.func_a)
上面這種方法,存在一些局限性。比如把一個函數直接賦值給實例時,無法正常創建綁定關系。
# 把函數賦值給實例 class_a.func_c = func_c # 沒有形成綁定關系 # <function func_c at 0x10e59f1e0> print(class_a.func_c)
所以,就需要引入MethodType,將一個函數綁定到實例或類上,形成綁定關系。
MethodType 會在類內部創建一個鏈接,指向外部的的方法,在創建實例的同時,這個綁定後的方法也會復制到實例中。MethodType 接受兩個參數,第一個是被綁定的函數,第二個是需要綁定到的對象。
class ClassB: pass class_b = ClassB() class_b.func_a = MethodType(func_a, class_b) print(class_b.func_a) # <bound method func_a of <__main__.ClassB object at 0x0000021706F6B780>> ClassB.func_b = MethodType(func_b, ClassB) print(ClassB.func_b) # <bound method func_b of <class ‘__main__.ClassB‘>>
經過代碼測試,成功把函數綁定到了類和實例上。
之前說過,MethodType只是一個鏈接指向外部函數,而不是把函數復制到類內部。
關於這點,可以用一個閉包來驗證。
# 閉包 def func_d(a): def _func_d(self, b): nonlocal a a = a + b print(a) print(self) return _func_d # a 初始值為1 test_func_d = func_d(1)
將閉包分別綁定到兩個截然不同的類創建出的實例上
class_a.test_func_d = MethodType(test_func_d, class_a)
class_b.test_func_d = MethodType(test_func_d, class_b)
兩個不同的實例的test_func_d,實際上是指向同一個函數。
分別調用兩個實例的test_func_d方法,得到3、6兩個結果。
class_a.test_func_d(2) # 3 class_b.test_func_d(3) # 6
第一次執行class_a.test_func_d(1)時,閉包中的變量 a 已經由 1 變為 1+2=3,第二次執行class_b.test_func_d(3)時,閉包中的變量 a 已經由 3 變為 3+3=6。
可見兩個實例執行的是同一個函數,共享閉包內的變量a,所以說是創建一個鏈接,指向函數,而不是把函數復制到類內部。
使用MethodType函數將方法綁定到類或實例上