Andrei Cioara's answer 基本正确:
随机性来自 Python 3.3 和更高版本的默认随机哈希顺序(请参阅 Why is dictionary ordering non-deterministic?)。
访问x会调用已经绑定到__getattribute__的lambda函数。
参见Difference between __getattr__ vs __getattribute__ 和the Python3 datamodel reference notes for object.__getattribute__。
我们可以使整个事情变得不那么令人困惑:
class t(object):
def __getattribute__(self, name):
use = None
for val in vars(object).values():
if callable(val) and type(val) is not type:
use = val
return use
def super_serious(obj):
proxy = t()
return proxy
这就是 lambda 所发生的事情。请注意,在循环中,我们不会绑定/保存 val 的 current 值。1 这意味着我们得到了 last val 在函数中的值。使用原始代码,我们在创建对象t 时完成所有这些工作,而不是稍后在调用t.__getattribute__ 时——但它仍然归结为: Of vars( object),找到最后一个符合我们条件的:该值必须是可调用的,而该值的类型不是它本身type。
使用class t(object) 使t 即使在Python2 中也成为一种新型类对象,因此这段代码现在在Python2 和Python3 中都“工作”了。当然,在 Py2k 中,字典排序不是随机的,所以我们每次总是得到相同的东西:
$ python2 foo3.py
<slot wrapper '__init__' of 'object' objects>
$ python2 foo3.py
<slot wrapper '__init__' of 'object' objects>
对比:
$ python3 foo3.py
<slot wrapper '__eq__' of 'object' objects>
$ python3 foo3.py
<slot wrapper '__lt__' of 'object' objects>
将环境变量PYTHONHASHSEED 设置为0 使得Python3 中的顺序也具有确定性:
$ PYTHONHASHSEED=0 python3 foo3.py
<method '__subclasshook__' of 'object' objects>
$ PYTHONHASHSEED=0 python3 foo3.py
<method '__subclasshook__' of 'object' objects>
$ PYTHONHASHSEED=0 python3 foo3.py
<method '__subclasshook__' of 'object' objects>
1要了解这是什么,请尝试以下操作:
def f():
i = 0
ret = lambda: i
for i in range(3):
pass
return ret
func = f()
print('func() returns', func())
请注意,它说的是func() returns 2,而不是func() return 0。然后将 lambda 行替换为:
ret = lambda stashed=i: stashed
然后再次运行它。现在函数返回 0。这是因为我们在此处保存了 i 的 current 值。
如果我们对示例程序做同样的事情,它将返回第一个符合条件的val,而不是最后一个。