问题来源
在使用peft进行LoRA调优的过程中,看见一行代码,让人摸不着头脑
model.state_dict = (
lambda self, *_, **__: get_peft_model_state_dict(self, old_state_dict())
).__get__(model, type(model))
结论
这行将lambda方法绑定给实例model,方法名为state_dict,它的类型为method
通常比较直观的给实例绑定函数是通过MethodType,__get__这个方法无法定位到源码,没接触过很难想到它是什么意思。
下面给出了两种给实例绑定方法的代码
from types import MethodType
class A:
pass
def func(self, *_, **__):
return 10
a = A()
a.f1 = MethodType(func, a)
a.f2 = (
lambda self, *_, **__: 20
).__get__(a, type(a))
res = a.f1()
print(res)
res = a.f2()
print(res)
10
20
如何理解__get__()
首先说明与上述示例无关的用法
官方文档中提到
object.__get__(*self*, *instance*, *owner=None*)
Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). The optional *owner* argument is the owner class, while *instance* is the instance that the attribute was accessed through, or `None` when the attribute is accessed through the *owner*.
This method should return the computed attribute value or raise an AttributeError exception.
调用以获取所有者类(类属性访问)或该类实例(实例属性访问)的属性。可选参数owner:类名,instance参数:可以通过owner获取属性时传None,需要通过实例获取属性时传实例(即看你需要获取的是实例的属性还是类的属性)。
这个方法的返回值是计算好的属性值,或抛出属性错误的异常。
可以看到这是一般object的__get__实现的功能。但是调用者是Function类呢(虽然python一切都是类,但是继承过程中可能会重写)?
接下来是与示例有关的用法
官方文档中提到
To support automatic creation of methods, functions include the `__get__()` method for binding methods during attribute access. This means that functions are non-data descriptors that return bound methods during dotted lookup from an instance. Here’s how it works:
class Function:
...
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return MethodType(self, obj)
可以看到,在实现上,func.__get__(a, type(a))等价于 MethodType(func, a)。
其实就是Function类重写了__get__这个descriptors,让它返回是调用者(Function实例)的实例方法。
附:
func本身是类型是<function>,func.__get__(a, type(a))之后,类型是<bound method>,意思应该是实例方法。