很多地方讲到Python里用"."添加方法到实例,有注意到添加之后,比正常的实例方法要多加一个argument,也就是要手动添加实例名作为第一个参数,系统没有自动传入,否则报错少一个argument。示例如下:
class MyClass():
pass
def m1(self, a1):
print(a1)
my_instance = MyClass()
my_instance.m1 = m1
my_instance.m1('test')
# >>> TypeError: m1() missing 1 required positional argument: 'a1'
my_instance.m1('for arg "self"', 'test')
# >>> test
# self参数的位置,一般应该传入my_instance。
# 但m1里面没有用到self,所以传入什么都行。
type(my_instance.m1)
# >>> function
# 即使绑定到实例,仍然不是bound method,属于unbound method
# 用起来像staticMethod
使用types.MethodType(function, instance, [class])绑定(class是可选),m1就变成了method:
import types
my_instance.m1 = types.MethodType(m1, my_instance)
my_instance.m1('test')
# >>> test
也可以用func.__get__(instanceObject)绑定,达到一样的效果。但是使用了不该用的私有方法。
my_instance.m1 = m1.__get__(my_instance)
直接给MyClass绑定m1也可以,这样所有的MyClass的实例都会立刻被修改有了m1方法,包括绑定之前产生的实例。还有很多其他奇怪的方法来实现类似绑定的效果,比如说重新定义my_instance.m1函数,给套个壳函数什么的就不说了。
实际上,如果不介意手动传入self参数到m1里,也没必要费事绑定成 实例方法。
类Class的属性,方法,实例instance的属性的绑定,都可以直接用通常的"."来绑定,只有实例的方法的绑定特殊一点。这些"."的绑定,也可以用setattr来实现。
setattr(Class/instance, 'attr/method name', value/function)
在一个class的一个方法func里绑定另一个方法 到另一个类的实例上,貌似直接 instance.new_method=self.func就好,因为 self. 就相当于把第一个参数传进去了。
import types
class MyClass():
def z(self):
self.a = 3
self.b = 4
class MyClass2():
def m1(self, my_instance):
# my_instance.m2 = types.MethodType(self.m2, my_instance)
my_instance.m2 = self.m2
self.a = 1
def m2(self, p):
self.b = 2
print(self.a)
print(self.b)
print(p)
my_instance = MyClass()
mc2 = MyClass2()
mc2.m1(my_instance)
# my_instance.m2 = mc2.m2
my_instance.m2('first_run')
my_instance.z()
print(my_instance)
print(my_instance.m2)
my_instance.m2('second_run')
# -=> 1
# -=> 2
# -=> first_run
# -=> <__main__.MyClass object at 0x000001F30015EE80>
# -=> <bound method MyClass2.m2 of <__main__.MyClass2 object at 0x000001F30015EEB0>>
# -=> 1
# -=> 2
# -=> second_run
可以看到,my_instance.m2仍然是MyClass2实例mc2的bound method绑定方法m2,会自动传入mc2的实例作为self参数。my_instance.m2只能访问mc2,不能访问my_instance实例。这是用 . 和 = 赋值。
把上面的例子变一下,把my_instance.m2 = self.m2换成my_instance.m2 = types.MethodType(self.m2, my_instance),就会出错TypeError: m2() takes 2 positional arguments but 3 were given。这里不演示这个TypeError。我们看看去掉自己传入的参数'first_run'看看打印出什么。
import types
class MyClass():
def z(self):
self.a = 3
self.b = 4
class MyClass2():
def m1(self, my_instance):
my_instance.m2 = types.MethodType(self.m2, my_instance)
# my_instance.m2 = self.m2
self.a = 1
def m2(self, p):
self.b = 2
print(self.a)
print(self.b)
print(p)
my_instance = MyClass()
mc2 = MyClass2()
mc2.m1(my_instance)
# my_instance.m2 = mc2.m2
print(my_instance.m2)
print('------1--------')
my_instance.m2()
print('-------2-------')
my_instance.z()
print('-------3-------')
print(my_instance)
print('-------4-------')
print(my_instance.m2)
print('------5--------')
my_instance.m2()
# -=> <bound method MyClass2.m2 of <__main__.MyClass object at 0x0000015DF8730040>>
# -=> ------1--------
# -=> 1
# -=> 2
# -=> <__main__.MyClass object at 0x0000015DF8730040>
# -=> -------2-------
# -=> -------3-------
# -=> <__main__.MyClass object at 0x0000015DF8730040>
# -=> -------4-------
# -=> <bound method MyClass2.m2 of <__main__.MyClass object at 0x0000015DF8730040>>
# -=> ------5--------
# -=> 1
# -=> 2
# -=> <__main__.MyClass object at 0x0000015DF8730040>
可以看出,整个mc2.m2被绑定到my_instance上,所以两个实例都会给m2传入自己的实例作为self参数,虽然m2只想接受1个。m2最终接受了最先传入的,也是最近的实例mc2;而my_instance被当作第二个参数传入,到最后一行被打印出来。
我们试试把my_instance.m2 = MyClass2.m2这个从类引用的函数(类函数),而不是实例方法mc2.m2。其他不变,直接执行。
import types
class MyClass():
def z(self):
self.a = 3
self.b = 4
class MyClass2():
def m1(self, my_instance):
# my_instance.m2 = types.MethodType(self.m2, my_instance)
my_instance.m2 = MyClass2.m2 # 这里变了
self.a = 1
def m2(self, p):
self.b = 2
print(self.a)
print(self.b)
print(p)
my_instance = MyClass()
mc2 = MyClass2()
mc2.m1(my_instance)
# my_instance.m2 = mc2.m2
print(my_instance.m2)
print('------1--------')
my_instance.m2()
print('-------2-------')
my_instance.z()
print('-------3-------')
print(my_instance)
print('-------4-------')
print(my_instance.m2)
print('------5--------')
my_instance.m2()
# -=> <function MyClass2.m2 at 0x000001F15A036EE0>
# -=> ------1--------
# -=> TypeError: my_instance.m2() missing 2 required positional arguments: 'self' and 'p'
出错了,没有传入任何参数。因为是类函数,相当于普通函数,不是方法,所以没有传入任何参数;也没有绑定到my_instance,所以没有传入自己实例。
我们再换成my_instance.m2 = types.MethodType(MyClass2.m2, my_instance),把参数p加上,my_instance.z()提前,否则self.a,b未赋值。
import types
class MyClass():
# a = 3
# b = 4
def z(self):
self.a = 3
self.b = 4
pass
class MyClass2():
# a = 1
# b = 2
def m1(self, my_instance):
my_instance.m2 = types.MethodType(MyClass2.m2, my_instance)
# my_instance.m2 = MyClass2.m2
self.a = 1
def m2(self, p):
self.b = 2
print(self.a)
print(self.b)
print(p)
my_instance = MyClass()
mc2 = MyClass2()
mc2.m1(my_instance)
# my_instance.m2 = mc2.m2
print(my_instance.m2)
print('------1--------')
my_instance.z()
my_instance.m2('first_run')
print('-------2-------')
my_instance.z()
print('-------3-------')
print(my_instance)
print('-------4-------')
print(my_instance.m2)
print('------5--------')
my_instance.m2('second_run')
# -=> <bound method MyClass2.m2 of <__main__.MyClass object at 0x0000013D3A80FE80>>
# -=> ------1--------
# -=> 3
# -=> 2
# -=> first_run
# -=> -------2-------
# -=> -------3-------
# -=> <__main__.MyClass object at 0x0000013D3A80FE80>
# -=> -------4-------
# -=> <bound method MyClass2.m2 of <__main__.MyClass object at 0x0000013D3A80FE80>>
# -=> ------5--------
# -=> 3
# -=> 2
# -=> second_run
现在my_instance.m2就像一个正常的my_instance实例方法一样了。显示为bound method MyClass2.m2 of my_instance,不会传入MyClass2的实例,my_instance会传入自身实例作为第一个参数。
总结,一个函数和哪个实例绑定了,就会传入那个实例当作参数,可以多层绑定,传入多个实例。从类引用方法不会传入这个类的实例,用types.MethodType可以绑定到其他实例上,就能正常工作。
这篇博客探讨了在Python中如何将函数绑定到实例作为方法,以及使用`types.MethodType`和`__get__`进行方法绑定。内容涉及实例方法、未绑定方法和静态方法的区别,以及在类的方法中动态绑定其他实例方法的细节,展示了不同绑定方式对参数传递的影响。

432

被折叠的 条评论
为什么被折叠?



