MethodType用法和setattr區別

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裏就不行。

目前區別不大(這裏存疑,後續有更新內容再更新)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章