将绑定方法传递给函数

时间:2015-08-29 15:43:00

标签: python methods variable-assignment argument-passing

我认为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循环产生一个有趣的模式,而第一个不产生。似乎分配绑定方法会改变绑定方法本身,但为什么不将它传递给函数?

2 个答案:

答案 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

当然,实际数字将反映系统在运行时使用的状态。