当执行`func = func .__ call__`时,Python会发生什么?

时间:2012-12-04 21:56:54

标签: python testing attributes crash call

最近在Python中出现了一些问题:x = y(z)相当于x = y.__call__(z)。但是,测试似乎使该假设无效,并导致Python的解释器崩溃。

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:55:48) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def ret(*args):
...     return args
...
>>> ret(1, 2, 3)
(1, 2, 3)
>>> for _ in range(1000000):
...     ret = ret.__call__
...
>>> ret(1, 2, 3)

运行第二个ret(1, 2, 3)会导致Python崩溃并返回命令提示符(image)。

  1. ret = ret.__call__执行时后台发生了什么?
  2. 为什么Python在最后一行停止工作,是否应该报告为错误?
  3. 无用的参考: Python functions and their __call__ attribute

1 个答案:

答案 0 :(得分:1)

您正在创建一个深层嵌套的方法包装器结构。每个方法包装器仍然引用self,其中self是对父方法包装器的引用,一直回到原始函数:

>>> ret, ret.__call__.__self__
(<function ret at 0x10f17a050>, <function ret at 0x10f17a050>)
>>> ret.__call__, ret.__call__.__call__.__self__
(<method-wrapper '__call__' of function object at 0x10f17a050>, <method-wrapper '__call__' of function object at 0x10f17a050>)

请注意方法包装器__self__属性的内存地址如何指向父对象。

如果您创建了足够的这些包装器,则可能会耗尽内存。

Python为绑定到实例的所有函数创建了这样的包装器。对于具有方法的自定义类,它是相同的:

>>> class Foo: 
...     def bar(self): return
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x10f1798d0>>
>>> Foo().bar, Foo().bar.__self__
(<bound method Foo.bar of <__main__.Foo object at 0x10f179710>>, <__main__.Foo object at 0x10f179850>)

通过属性访问访问方法时,根据需要从函数创建方法。因为它们包含对self的引用,所以只要您对它们有引用,它们就会保留在内存中。因此,您的引用链可以保存100000个内存包装器。