我认为func(obj.attr)
和tmp = obj.attr; func(tmp)
应完全无法区分。但是,以下代码
class Cls(object):
#@staticmethod
def f(self):
pass
def who(obj):
print(id(obj) % 1000, end=' ')
obj = Cls()
n = 10
for i in range(n):
who(obj.f)
print('\n')
for i in range(n):
tmp = obj.f
who(tmp)
产生
736 736 736 736 736 736 736 736 736 736
736 216 736 216 736 216 736 216 736 216
我们可以看到第二个for循环产生一个有趣的模式,而第一个不产生。似乎分配绑定方法会改变绑定方法本身,但为什么不将它传递给函数?
答案 0 :(得分:2)
每次访问f
时,Python都会创建一个新绑定方法对象。(参见Does Python really create all bound method for every new instance?)。
在第一个循环中:
obj.f
创建绑定方法对象。who
打印ID obj.f
创建一个新的绑定方法对象。 intrepreter在之前重用迭代中释放的内存。who
打印发生的ID以匹配之前的ID 在第二个循环中:
obj.f
创建一个新的绑定方法对象tmp
(因此它现在有一个参考)who
打印ID tmp
变量使对象保持活动状态obj.f
创建一个新的绑定方法对象。 它无法重复使用旧地址,因为该对象仍处于活动状态。 tmp
。 现在tmp
的旧值没有更多引用,因此被取消分配。 who
打印ID ,这是一个新的。obj.f
创建一个新的绑定方法对象。现在旧的地址再次可用,解释器决定重复使用它。tmp
。 现在tmp
的旧值没有更多引用,因此被取消分配。 who
打印ID ,这是一个旧的 因此创建了一个在两个地址之间交替的循环。
通过使用更多变量,您可以创建更长的周期:
>>> tmp1 = obj.f
>>> tmp2 = tmp3 = tmp4 = tmp5 = None
>>> for i in range(20):
... tmp5 = tmp4
... tmp4 = tmp3
... tmp3 = tmp2
... tmp2 = tmp1
... tmp1 = obj.f
... who(tmp1)
...
864 896 560 280 288 864 896 560 280 288 864 896 560 280 288 864 896 560 280 288
或者,正如Reblochon Masque在他的answer中提到的,使用del
你可以避免这种行为:
>>> for i in range(20):
... tmp = obj.f
... who(tmp)
... del tmp # forget the name
...
688 688 688 688 688 688 688 688 688 688 688 688 688 688 688 688 688 688 688 688
请注意,这是由于CPython的实现细节。在没有引用计数的Jython或其他python实现中,两个循环可能会以相同的方式运行:显示十个不同的ID。
答案 1 :(得分:1)
不是答案,而是Bakuriu'的补充。回答:
如果在继续迭代之前删除temp,则会得到与案例1中相同的结果:
(...)
for i in range(n):
tmp = obj.f
who(tmp)
del(tmp)
产生
496 496 496 496 496 496 496 496 496 496
496 496 496 496 496 496 496 496 496 496
当然,实际数字将反映系统在运行时使用的状态。