Mr. F - 6 months ago 21

Python Question

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

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

Could I change this behavior for a class, such that

`a <= b <= c`

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

`__gt__`

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)

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

1) I am

`and`

2) For an analogy to what I want to do, consider the

`with`

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

`with`

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

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

Answer

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