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里就不行。
目前区别不大(这里存疑,后续有更新内容再更新)