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

目前区别不大(这里存疑,后续有更新内容再更新)

 

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