如何在不修改类C的前提下让传入函数以方法形式调用?
问题背景与代码示例
编辑说明
我扩展了示例以展示之前提到的更复杂场景,感谢评论区的反馈。
补充背景
- 我主要从理论层面关注这个问题,也接受诸如“由于……原因无法实现”的答案,并希望能了解Python内部机制的详细解释。
- 针对我的具体问题已有解决方案,该库允许传入类来实现需求,但如果能通过仅传入可绑定函数的更简洁接口实现,将节省大量代码。(感兴趣者须知:此问题源自SQLAlchemy扩展associationproxy的creator函数参数。)
第三方库代码(mylib.py)
# mylib.py from typing import Callable class C: def __init__(self, foo: Callable): self.foo = foo def __get__(self, instance, owner): # simplified return CConcrete(self.foo) class CConcrete: foo: Callable def __init__(self, foo: Callable): self.foo = foo def bar(self): return self.foo()
业务代码(main.py)
# main.py from mylib import C def my_foo(self): return True if self else False class House: window = C(my_foo) my_house = House() print(my_house.window.bar())
运行报错
my_foo() missing 1 required positional argument: 'self'
核心问题
如何在不修改类C本身的前提下,让my_foo被调用时传入self?
注:类C属于第三方库,无法修改。
解决方案与原理解析
问题根源
Python描述符的工作逻辑是:访问my_house.window时,C.__get__被触发,返回一个CConcrete实例,但这个过程里,原House实例my_house既没传递给CConcrete,也没和my_foo完成绑定。CConcrete.bar()调用self.foo()时,只是直接执行原始函数,没有传入任何参数,因此会因缺少self参数报错。
可行方案:闭包捕获实例+包装函数
通过自定义两层包装的闭包,在描述符的__get__阶段捕获House实例,再将其传递给my_foo。修改main.py代码如下:
# main.py from mylib import C def my_foo(self): return True if self else False def wrap_foo(target_func): def capture_instance(instance): def call_with_instance(): return target_func(instance) return call_with_instance return capture_instance class House: window = C(wrap_foo(my_foo))
也可以用更简洁的lambda写法:
# main.py from mylib import C def my_foo(self): return True if self else False class House: window = C(lambda inst: lambda: my_foo(inst))
原理说明
- 类定义阶段,
wrap_foo(my_foo)返回capture_instance函数,被传入C的构造函数。 - 访问
my_house.window时,C.__get__被调用,此时instance就是my_house,它将self.foo(即capture_instance)传递给CConcrete。 CConcrete.bar()调用self.foo()时,实际是执行capture_instance(my_house),返回call_with_instance函数;最终call_with_instance()执行时,会把my_house作为self参数传入my_foo,满足参数要求。
为什么不能直接绑定?
因为C.__get__的逻辑是直接将原始foo传递给CConcrete,没有预留绑定实例的逻辑。如果能修改C,可以在__get__中将instance传递给foo或完成绑定,但由于C是第三方库不可修改,只能通过两层闭包的方式,先捕获实例,再返回无参函数供CConcrete.bar()调用。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

