1. 程式人生 > >使用MethodType函數將方法綁定到類或實例上

使用MethodType函數將方法綁定到類或實例上

變量 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函數將方法綁定到類或實例上