开发者

what methods does `foo < bar < baz` actually invoke?

开发者 https://www.devze.com 2023-01-25 05:29 出处:网络
In python we can say: if foo < bar < baz: do something. and similarly, we can overload the comparision operators like:

In python we can say:

if foo < bar < baz:
    do something.

and similarly, we can overload the comparision operators like:

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

but what methods of the types of the operands of those interval co开发者_StackOverflowmparisions are actually called? is the above equivalent to

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

Edit: re S.Lott, Here's some output that helps to illustrate what actually happens.

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


if foo < bar < baz:

is equivalent to

if foo < bar and bar < baz:

with one important distinction: if bar is a mutating, it will be cached. I.e.:

if foo < bar() < baz:

is equivalent to

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

But to answer your question, it will end up being:

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


You are correct:

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

Output:

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


It uses successive calls to the less-than comparison operator:

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


It calls the special method __lt__(), and if needed it will call __nonzero__() to coerce the result of __lt__() to a boolean. Surprisingly (to me at least), there is no __and__() method to override the and operator.

Here's a test program:

#!/usr/bin/env python

class Bar:
    def __init__(self, value):
        self.value = value

    def __lt__(self, other):
        print "%s.__lt__(%s)" % (self, other)
        return Bar("%s.__lt__(%s)" % (self, other))

    def __nonzero__(self):
        print "%s.__nonzero__()" % (self)
        return True

    def __str__(self):
        return self.value

foo = Bar("foo")
bar = Bar("bar")
baz = Bar("baz")

if foo < bar < baz:
    pass

Output:

foo.__lt__(bar)
foo.__lt__(bar).__nonzero__()
bar.__lt__(baz)
bar.__lt__(baz).__nonzero__()
0

精彩评论

暂无评论...
验证码 换一张
取 消