What is the operator precedence when writing a dou

2019-04-22 15:02发布

What is the specific code, in order, being executed when I ask for something like

>>> 1 <= 3 >= 2
True

If both have equal precedence and it's just the order of their evaluation, why does the second inequality function as (3 >= 2) instead of (True >= 2)

Consider for example the difference between these

>>> (1 < 3) < 2
True

>>> 1 < 3 < 2
False

Is it just a pure syntactical short-cut hard-coded into Python to expand the second as the and of the two statements?

Could I change this behavior for a class, such that a <= b <= c gets expanded to something different? It's looking like the following is the case

a (logical operator) b (logical operator) c 
    --> (a logical operator b) and (b logical operator c)

but the real question is how this gets implemented in code.

I'm curious so that I can replicate this kind of __lt__ and __gt__ behavior in some of my own classes, but I am confused about how this is accomplished holding the middle argument constant.

Here's a specific example:

>>> import numpy as np

>>> tst = np.asarray([1,2,3,4,5,6])

>>> 3 <= tst
array([False, False,  True,  True,  True,  True], dtype=bool)

>>> 3 <= tst <= 5
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/home/ely/<ipython-input-135-ac909818f2b1> in <module>()
----> 1 3 <= tst <= 5

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

It would be nice to override this so that it "just works" with arrays too, like this:

>>> np.logical_and(3 <= tst, tst <= 5)
array([False, False,  True,  True,  True,  False], dtype=bool)

Added for clarification

In the comments it is indicated that I did a poor job of explaining the question. Here's some clarifying remarks:

1) I am not looking for a simple explanation of the fact that the interpreter pops an and in between the two chained inequalities. I already knew that and said so above.

2) For an analogy to what I want to do, consider the with statement (link). The following:

with MyClass(some_obj) as foo:
    do_stuff()

unpacks into

foo = MyClass(some_obj)
foo.__enter__()
try:
    do_stuff()
finally:
    foo.__exit__()

So by writing MyClass appropriately, I can do many special things inside of the with statement.

I am asking whether there is a similar code unpacking of the chained inequality by which I can intercept what it's doing and redirect it to use array-style logical operators instead just for the classes I care about.

I feel this is very clear from my question, especially the example, but hopefully this makes it more clear.

3条回答
不美不萌又怎样
2楼-- · 2019-04-22 15:37

but the real question is how this gets implemented in code.

Do you mean how the interpreter transforms it, or what? You already said

a (logical operator) b (logical operator) c 
    --> (a logical operator b) and (b logical operator c)

so I'm not sure what you're asking here OK, I figured it out: no, you cannot override the expansion from a < b < c into (a < b) and (b < c) IIUC.


I'm curious so that I can replicate this kind of __lt__ and __gt__ behavior in some of my own classes, but I am confused about how this is accomplished holding the middle argument constant.

It depends which of a, b and c in the expression a < b < c are instances of your own class. Implementing your __lt__ and __gt__ and methods gets some of the way, but the documentation points out that:

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does)

So, if you want Int < MyClass < Int, you're out of luck. You need, at a minimum, MyClass < MyClass < Something (so an instance of your class is on the LHS of each comparison in the expanded expression).

查看更多
Viruses.
3楼-- · 2019-04-22 15:40

I'm not totally sure what you're looking for, but a quick disassembly shows that a < b < c is not compiled to the same bytecode as a < b and b < c

>>> import dis
>>>
>>> def f(a, b, c):
...     return a < b < c
...
>>> dis.dis(f)
  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_OR_POP    21
             14 LOAD_FAST                2 (c)
             17 COMPARE_OP               0 (<)
             20 RETURN_VALUE
        >>   21 ROT_TWO
             22 POP_TOP
             23 RETURN_VALUE
>>>
>>> def f(a, b, c):
...     return a < b and b < c
...
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 COMPARE_OP               0 (<)
              9 JUMP_IF_FALSE_OR_POP    21
             12 LOAD_FAST                1 (b)
             15 LOAD_FAST                2 (c)
             18 COMPARE_OP               0 (<)
        >>   21 RETURN_VALUE

Edit 1: Digging further, I think this is something weird or wrong with numpy. Consider this example code, I think it works as you would expect.

class Object(object):
    def __init__(self, values):
        self.values = values
    def __lt__(self, other):
        return [x < other for x in self.values]
    def __gt__(self, other):
        return [x > other for x in self.values]

x = Object([1, 2, 3])
print x < 5 # [True, True, True]
print x > 5 # [False, False, False]
print 0 < x < 5 # [True, True, True]

Edit 2: Actually this doesn't work "properly"...

print 1 < x # [False, True, True]
print x < 3 # [True, True, False]
print 1 < x < 3 # [True, True, False]

I think it's comparing boolean values to numbers in the second comparison of 1 < x < 3.

Edit 3: I don't like the idea of returning non-boolean values from the gt, lt, gte, lte special methods, but it's actually not restricted according to the Python documentation.

http://docs.python.org/reference/datamodel.html#object.lt

By convention, False and True are returned for a successful comparison. However, these methods can return any value...

查看更多
对你真心纯属浪费
4楼-- · 2019-04-22 15:49

Both have the same precedence, but are evaluated from left-to-right according to the documentation. An expression of the form a <= b <= c gets expanded to a <= b and b <= c.

查看更多
登录 后发表回答