`foo < bar < baz` 实际调用了哪些方法?

9 投票
4 回答
1404 浏览
提问于 2025-04-16 07:11

在Python中,我们可以这样写:

if foo < bar < baz:
    do something.

而且,我们也可以重载比较运算符,比如:

class Bar:
    def __lt__(self, other):
        do something else

但是,实际上在这些区间比较中,操作数的类型调用了哪些方法呢?上面的写法等同于

if foo.__lt__(bar) and bar.__lt__(baz):
    do something.

编辑:关于S.Lott,这里有一些输出结果,可以帮助说明实际发生了什么。

>>> class Bar:
    def __init__(self, name):
        self.name = name
        print('__init__', self.name)
    def __lt__(self, other):
        print('__lt__', self.name, other.name)
        return self.name < other.name

>>> Bar('a') < Bar('b') < Bar('c')
('__init__', 'a')
('__init__', 'b')
('__lt__', 'a', 'b')
('__init__', 'c')
('__lt__', 'b', 'c')
True
>>> Bar('b') < Bar('a') < Bar('c')
('__init__', 'b')
('__init__', 'a')
('__lt__', 'b', 'a')
False
>>> 

4 个回答

3

它使用了连续的调用小于比较运算符:

>>> import dis
>>> def foo(a,b,c):
...     return a < b < c
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 DUP_TOP             
              7 ROT_THREE           
              8 COMPARE_OP               0 (<)
             11 JUMP_IF_FALSE            8 (to 22)
             14 POP_TOP             
             15 LOAD_FAST                2 (c)
             18 COMPARE_OP               0 (<)
             21 RETURN_VALUE        
        >>   22 ROT_TWO             
             23 POP_TOP             
             24 RETURN_VALUE        
13
if foo < bar < baz:

等同于

if foo < bar and bar < baz:

但有一个重要的区别:如果 bar 是一个会改变的东西,它会被缓存。也就是说:

if foo < bar() < baz:

等同于

tmp = bar()
if foo < tmp and tmp < baz:

不过为了回答你的问题,最后会变成:

if foo.__lt__(bar) and bar.__lt__(baz):
4

你说得对:

class Bar:
    def __init__(self, name):
        self.name = name
    def __lt__(self, other):
        print('__lt__', self.name, other.name)
        return True

a,b,c = Bar('a'), Bar('b'), Bar('c')

a < b < c

输出结果是:

('__lt__', 'a', 'b')
('__lt__', 'b', 'c')
True

撰写回答