Bound方法是通过实例引用(如a.foo)生成的已绑定self的对象,Unbound方法是通过类名引用(如A.foo)得到的未绑定函数,需显式传入实例;本质由描述器协议在运行时动态决定。

区分 Bound 与 Unbound 方法引用,关键不在“写法多复杂”,而在于“谁在调用、有没有实例参与”。核心就一点:有实例对象参与的引用是 Bound,没实例、只靠类名访问的就是 Unbound。
看引用方式就能判断 Bound 还是 Unbound
Python 中方法不是天生“绑定”或“不绑定”的——它取决于你如何获取这个方法对象:
-
Bound Method:通过实例获取,例如
a.foo。此时返回的是一个已把a自动塞进self的可调用对象; -
Unbound Method(Python 2)或普通函数(Python 3):通过类名获取,例如
A.foo。它只是原始函数本身,没有预设self,调用时必须手动传入实例(如A.foo(a)); - 注意:
A.foo在 Python 3 中类型是function,不再是instancemethod;而a.foo在 Python 2/3 中都是method类型。
底层其实是描述器(descriptor)在起作用
当你写 a.foo 或 A.foo,Python 并不是直接从 __dict__ 拿出函数返回。类属性访问会触发 __get__ 描述器协议:
- 函数对象自带
__get__方法,当它作为类属性被访问时,就会根据是否有 instance 参数决定返回什么; -
A.foo→ 调用foo.__get__(None, A)→ 返回未绑定的函数(Python 3)或 unbound method(Python 2); -
a.foo→ 调用foo.__get__(a, A)→ 返回绑定了a的 method 对象(即PyMethodObject); - 这个机制和
@property是同一套逻辑,只是行为不同。
变量绑定的本质是对象封装,不是语法糖
所谓“绑定”,不是编译期固定死的规则,而是运行时构造新对象的过程:
-
a.foo每次访问都会新建一个PyMethodObject,里面存着im_self = a、im_func = foo、im_class = A; - 这个对象实现了
__call__,调用它等价于foo(a, ...),就像functools.partial(foo, a); - 所以
id(a.foo)多次调用通常不同——因为每次都在创建新对象,哪怕内容一样; - 如果你把
a.foo赋给变量(如f = a.foo),那这个绑定就“固化”下来了,后续调用都复用同一个对象。
实战中怎么避免踩坑
真正容易出错的地方,往往出现在动态调用、回调、装饰器或序列化场景:
- 别对
A.foo直接加括号调用(A.foo()),除非你明确知道它不需要self(比如是@staticmethod); - 向线程、定时器或事件系统传递方法时,优先用
a.foo(Bound),避免手动传实例; - 若需保存方法供后期调用,建议用
lambda: a.foo()或functools.partial(A.foo, a),比反复取a.foo更稳定; - 检查方法类型可用
inspect.ismethod()(对 Bound)或inspect.isfunction()(对 Unbound / staticmethod),而不是靠名字或报错猜。










