python2
types.MethodType在python2和python3上實現有很大不同,整理下自己的理解,以及遇到的一些問題。
MethodType可以幫助將方法綁定到對象上,因爲python的所有內容都可以被視爲對象,所以這裏含義比較廣泛,比如可以給類對象綁定方法,也可以給類綁定方法。
下面給的實例是python2給一個對象,給一個類,綁定方法
#coding=utf-8
import types
class Dog():
def __init__(self, name):
self.name = name
def wangwang(self):
print self.name + " wangwang!"
def wangwang(self):
print self.name + " houhou!"
dog = Dog("wangcai")
dog.wangwang()
# 給類對象增加方法
dog.wangwang = types.MethodType(wangwang, dog)
dog.wangwang()
# output: wangcai wangwang!
print dog.wangwang
print Dog.wangwang
dog2 = Dog("xiaoqiang")
dog2.wangwang()
# output: xiaoqiang wangwang!
Dog.wangwang = types.MethodType(wangwang, None,Dog)
dog2.wangwang()
# xiaoqiang houhou!
print Dog.wangwang
# <unbound method Dog.wangwang>
print dog2.wangwang
# <bound method Dog.wangwang of <__main__.Dog instance at 0x000000000348ED88>>
在stackoverflow上有一個比較巧妙的實現,將一個A類的綁定方法綁定到B上,B就可以將A和B的方法集中的綁定到一起了
https://stackoverflow.com/questions/12177405/python2-vs-python3-function-to-method-binding
#https://stackoverflow.com/questions/12177405/python2-vs-python3-function-to-method-binding
class A(object):
def method(self, other):
print self, other
class B(object): pass
B.method = types.MethodType(A().method, None, B)
B().method() # print both A and B instances
原題目最後一個有點問題,應該是少寫了創建實例的括號。
在上面這種方案中,將A類的實例的方法綁定到B上,這樣B的實例就會在method中輸出A的實例對象,B的實例對象,相當於B的method方法,第一個參數隱藏的是A的實例的self。
如果在python3中就無法實現了。
在python2中MethodType中實現如下:
class _C:
def _m(self): pass
ClassType = type(_C)
UnboundMethodType = type(_C._m) # Same as MethodType
_x = _C()
InstanceType = type(_x)
MethodType = type(_x._m)
MethodType是一個instancemethod,其實是一個bounding method的類型。
在python2中,MethodType有三個參數,第一個是傳入的是方法,第二個是實例,可以爲None或者爲空,第三個是類。不同的傳入參數,指定的綁定對象不同,參數指向的空間也不同。
但是在python3中,這個實現改變了
python3
class MethodType:
__func__: _StaticFunctionType
__self__: object
__name__: str
__qualname__: str
def __init__(self, func: Callable, obj: object) -> None: ...
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
MethodType變成了一個<class 'method'>,只有兩個參數,第一個callable的func,第二個是一個對象(因爲python中都是對象),如何實現上面StackOverflow的這個內容呢,在上面的那個內容中,回答者給出的幾個方案,我覺得應該都是錯的,因爲順序錯了。反倒是提問者給了一個自己的實現。
class UnboundMethod:
"""unbound method wrapper necessary for python3 where we can't turn
arbitrary object into a method (no more unbound method and only function
are turned automatically to method when accessed through an instance)
"""
def __init__(self, callable):
self.callable = callable
def __get__(self, instance, objtype):
if instance is None:
return self.callable
return types.MethodType(self.callable, instance)
B.method = UnboundMethodType(A().method)
B().method() # print both A and B instances
B.method = lambda o: A.method(o,A())
b = B()
b.method()
python3中更簡化了,只能對對象進行綁定方法,但是我這裏有個疑問,不過至今沒有解決,目前的用法也沒有什麼問題。
因爲考慮到如果一個自定義函數的self參數和類裏的self總歸是有所不同的。
通過
types.MethodType(wangwang, None,Dog)
這種方式實現,可以將某個方法徹底的綁定到類上。兩個實例方法指向的函數地址都一樣,類型也一樣
<bound method Dog.wangwang of <__main__.Dog instance at 0x0000000002E4EF08>>
但是如果是python3實現,則出現了下列的結果,兩次方法不一樣
<bound method Dog.wangwang of <__main__.Dog object at 0x0000022212D91DC8>>
<bound method wangwang of <class '__main__.Dog'>>
通過綁定,方法變成了一個類的靜態方法,和java的靜態方法有些類似。
MethodType將方法綁定到類上,並不是將這個方法寫到這個類的內部,而是在內存中船艦一個link指向外部的方法,創建Stu的實例的時候,這個link也會被複制,但是不管創建多少實例,指向的都是同一個方法,這個就是靜態方法了,相信大家用classmethod裝飾器試試,是一樣的效果,只是classmethod會把函數綁定到這個類上。
很多資料說的python的類似打補丁的方式來用MethodType,在python3中有一些改變。網上太多資料,抄來抄去,氾濫了。這裏記錄下,後續有有的方式可以記錄下。
setattr
在一部分,有一塊時關於__get__方法的,這個是類的內嵌方法,用於獲取屬性。這裏涉及比較多的類實例訪問屬性順序的介紹,這裏轉載下
https://www.cnblogs.com/andy1031/p/10923834.html
拋開這個內容,繼續setattr
print(dog.wangwang)
setattr(Dog, "wangwang", wangwang)
print(dog.wangwang)
print(Dog("xiaoqiang").wangwang)
#<bound method Dog.wangwang of <__main__.Dog object at 0x0000028980025188>>
#<bound method wangwang of <__main__.Dog object at 0x0000028980025188>>
#<bound method wangwang of <__main__.Dog object at 0x0000028980025248>>
如果我們用setattr,也是可以實現MethodType的方案的。setattr給對象設置屬性,這個屬性不限制方法,屬性值等。在python2中MethodType綁定方法時,可以實現將bouding method綁定到另外一個對象上,實現一些特殊方案,但是python3裏就不行。
目前區別不大(這裏存疑,後續有更新內容再更新)