Python函数存入类后变为unbound method的原因与解决办法
这问题其实是Python 2里的一个经典“坑”——当你把普通函数赋值给类属性时,Python 2会自动把它包装成未绑定方法(unbound method),而不是保留它原本的普通函数形态,这就导致你调用的时候,它会要求传入类实例,直接传字符串自然就触发TypeError了。
咱们看你的代码:你用TestEnvironment.setOnOut(log)把全局的log函数存到类的静态属性_on_out里,但Python 2悄悄把这个函数转换成了类的未绑定方法,所以当你调用TestEnvironment.onOut()("A test")时,它会误以为你要调用类的方法,非要你传个TestEnvironment实例当第一个参数。
解决办法有两种,看你需求选:
1. 升级到Python 3(最省心)
Python 3已经彻底移除了这个自动包装的行为,普通函数赋值给类属性后还是普通函数,直接就能用,不需要改任何代码。
2. 如果必须留在Python 2,修改setOnOut方法
直接操作类的__dict__来赋值,跳过Python 2的方法包装机制:
class TestEnvironment(object): _on_out = None # ... @staticmethod def onOut(): return TestEnvironment._on_out @staticmethod def setOnOut(on_out): # 直接操作类字典,避免自动包装成未绑定方法 TestEnvironment.__dict__['_on_out'] = on_out # 剩下的代码不变 def log(msg, process = None): print msg if __name__ == '__main__': TestEnvironment.setOnOut(log) print log print TestEnvironment.onOut() TestEnvironment.onOut()("A test")
运行这段代码,输出就会变成:
<function log at 0x7fd7738122a8> <function log at 0x7fd7738122a8> A test
完美解决问题。
为什么这样有效?
Python 2在访问类属性时,如果属性是函数,会自动触发方法包装逻辑,把函数转成未绑定方法。但直接通过__dict__给类属性赋值,会绕开这个逻辑,直接把原始的函数对象存进去,所以你取出来的时候还是普通函数,能直接调用。
另外还有个临时的 workaround:调用的时候取方法的__func__属性,比如TestEnvironment.onOut().__func__("A test"),但这种写法不够优雅,还是修改setOnOut的实现更靠谱。
内容的提问来源于stack exchange,提问作者Elad Weiss

