
在python中,使用==而非is比较实例方法(如obj.method)是正确做法,因为每次访问都会创建新的绑定方法对象,它们内存地址不同但逻辑相等;is仅适用于单例或同一对象引用的场景。
在python中,使用==而非is比较实例方法(如obj.method)是正确做法,因为每次访问都会创建新的绑定方法对象,它们内存地址不同但逻辑相等;is仅适用于单例或同一对象引用的场景。
为什么 is 比较绑定方法总是失败?
当你写 self.mpositioner.movetostart 时,Python 并非返回一个“固定”的对象,而是动态生成一个绑定方法(bound method)对象——它内部封装了两个关键信息:
- __func__:原始函数(类定义中的方法)
- __self__:被绑定的实例(即 self.mpositioner)
每次访问 obj.method,Python 都会新建一个 bound method 实例(即使连续访问同一属性)。这意味着:
>>> foo = Foo() >>> foo.bar is foo.bar # ❌ 总是 False False >>> id(foo.bar) == id(foo.bar) # ⚠️ 不可靠:可能为 True(因对象快速回收复用内存) True # 但这不代表是同一对象!
而 == 比较则由 bound method 类型的 __eq__ 方法实现,其逻辑是:
✅ 仅当 __func__ 相同且 __self__ 是同一对象时,才返回 True。
这正是你业务所需的语义:
“这个回调是否确实指向 mpositioner 实例的 movetostart 方法?”
立即学习“Python免费学习笔记(深入)”;
因此,以下判断是安全且语义准确的:
if func_to_execute == self.mpositioner.movetostart:
worker.signals.progress.connect(self.create_raw_log_line)✅ 正确实践示例
class MotorPositioner:
def movetostart(self):
return "moving to start..."
class Window:
def __init__(self):
self.mpositioner = MotorPositioner()
def thread_it(self, func_to_execute):
# ✅ 安全:比较逻辑等价性(相同实例 + 相同方法)
if func_to_execute == self.mpositioner.movetostart:
print("Matched movetostart callback")
# ❌ 危险:即使语义相同,也几乎总为 False
# if func_to_execute is self.mpositioner.movetostart: # 不要这样写!
# ✅ 替代方案(更显式、可读性高):
if (hasattr(func_to_execute, '__func__') and
hasattr(func_to_execute, '__self__') and
func_to_execute.__func__ is MotorPositioner.movetostart and
func_to_execute.__self__ is self.mpositioner):
print("Explicit bound method check")⚠️ 注意事项与建议
- 永远不要用 is 比较绑定方法:这是 Python 的语言机制决定的,不是 bug,而是设计使然。
- == 是标准且推荐的方式:它已被 Python 内置类型正确定义,符合直觉和实际需求。
- 避免过度优化:有人试图缓存 self.mpositioner.movetostart 到局部变量再用 is 比较,虽技术上可行,但牺牲可读性且引入额外状态,不推荐。
- Linter 差异说明:VS Code Pylint 建议 is 是因其默认将方法视为“不可变函数”,未充分建模绑定方法的动态性;Ruff 更贴近运行时行为,故不告警——此时应信任运行时语义而非静态分析的简化假设。
-
调试技巧:验证绑定关系可直接检查属性:
print(func_to_execute.__func__) # 应为 <function MotorPositioner.movetostart> print(func_to_execute.__self__) # 应为 self.mpositioner 实例
总结
| 比较方式 | 适用场景 | 是否适用于 obj.method | 原因 |
|---|---|---|---|
| is | 单例、None、明确同一对象引用 | ❌ 不适用 | 每次访问生成新 bound method 对象 |
| == | 逻辑相等性判断(推荐) | ✅ 完全适用 | bound method.__eq__ 正确比较 __func__ 和 __self__ |
掌握这一机制,不仅能解决当前回调匹配问题,更能加深对 Python 描述符协议、方法绑定及对象生命周期的理解——这是写出健壮、可维护 Python 代码的重要基础。










