Python中链式比较的“只评估一次”是什么意思?

19 投票
2 回答
3952 浏览
提问于 2025-04-15 15:37

一个朋友提醒了我这件事,后来我指出了一个奇怪的地方,我们俩都感到困惑。

Python的文档说,自从至少2.5.1版本以来(我没有查更早的版本):

比较可以任意链接,比如,x < y <= z 相当于 x < y 和 y <= z,除了 y 只会被计算一次(但在 x < y 为假时,z 根本不会被计算)。

我们困惑的地方在于“y 只被计算一次”这句话的意思。

考虑一个简单但有点牵强的类:

class Magic(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val
    def __lt__(self, other):
        print("Magic: Called lt on {0}".format(self.name))
        if self.val < other.val:
            return True
        else:
            return False
    def __le__(self, other):
        print("Magic: Called le on {0}".format(self.name))
        if self.val <= other.val:
            return True
        else:
            return False

我们可以得到这个结果:

>>> x = Magic("x", 0)
>>> y = Magic("y", 5)
>>> z = Magic("z", 10)
>>> 
>>> if x < y <= z:
...     print ("More magic.")
... 
Magic: Called lt on x
Magic: Called le on y
More magic.
>>> 

这看起来确实像是 'y' 在传统意义上被“计算”了两次——第一次是在调用 x.__lt__(y) 进行比较时,第二次是在调用 y.__le__(z) 时。

那么,考虑到这一点,Python文档中说的“y 只被计算一次”到底是什么意思呢?

2 个回答

8

在这里,y被用来表示一个可能会产生副作用的任意表达式。简单来说,y可以是任何代码,它在运行时可能会改变一些东西或者产生一些影响。举个例子:

class Foo(object):
    @property
    def complain(self):
        print("Evaluated!")
        return 2

f = Foo()
print(1 < f.complain < 3) # Prints evaluated once
print(1 < f.complain and f.complain < 3)  # Prints evaluated twice
45

这个'表达式' y 只会被计算一次。也就是说,在下面这个表达式中,函数只会执行一次。

>>> def five():
...    print 'returning 5'
...    return 5
... 
>>> 1 < five() <= 5
returning 5
True

和下面这个相比:

>>> 1 < five() and five() <= 5
returning 5
returning 5
True

撰写回答