Python Forum

Full Version: 0 == 0 is 0
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi,
I don't understand why 0 == 0 is 0 returns True ? What is the order of priority ?
Although (0==0) is 0 returns False and 0 == (0 is 0) returns False too.

Tx
From the docs

Quote:Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

So, what you have is (0 == 0) and (0 is 0) (note, braces are redundant, just for clarity)


Otherwise equality and identity operators have same priority
When I run 0==0 is 0, my Python complains about using "is" with a literal. It accepts the program, but says "I have a bad feeling about this."

I used the dis.dis() to look at the bytecodes
from dis import dis

def x():
    0 == 0 is 0

def y():
    (0 == 0) is 0

def z():
    0 == (0 is 0)

print(dis(x))
print(dis(y))
print(dis(z))
Output:
4 0 LOAD_CONST 1 (0) 2 LOAD_CONST 1 (0) 4 DUP_TOP 6 ROT_THREE 8 COMPARE_OP 2 (==) 10 JUMP_IF_FALSE_OR_POP 18 12 LOAD_CONST 1 (0) 14 COMPARE_OP 8 (is) 16 JUMP_FORWARD 4 (to 22) >> 18 ROT_TWO 20 POP_TOP >> 22 POP_TOP 24 LOAD_CONST 0 (None) 26 RETURN_VALUE None 7 0 LOAD_CONST 1 (0) 2 LOAD_CONST 1 (0) 4 COMPARE_OP 2 (==) 6 LOAD_CONST 1 (0) 8 COMPARE_OP 8 (is) 10 POP_TOP 12 LOAD_CONST 0 (None) 14 RETURN_VALUE None 10 0 LOAD_CONST 1 (0) 2 LOAD_CONST 1 (0) 4 LOAD_CONST 1 (0) 6 COMPARE_OP 8 (is) 8 COMPARE_OP 2 (==) 10 POP_TOP 12 LOAD_CONST 0 (None) 14 RETURN_VALUE None
I cannot write different Python code that generates the same bytecode, but this turns out to be a closer match:
from dis import dis

def x():
    return (0 == 0 is 0)

def y():
    if 0 == 0:
        return 0 is 0
    return False

print(dis(x))
print(dis(y))
Output:
4 0 LOAD_CONST 1 (0) 2 LOAD_CONST 1 (0) 4 DUP_TOP 6 ROT_THREE 8 COMPARE_OP 2 (==) 10 JUMP_IF_FALSE_OR_POP 18 12 LOAD_CONST 1 (0) 14 COMPARE_OP 8 (is) 16 RETURN_VALUE >> 18 ROT_TWO 20 POP_TOP 22 RETURN_VALUE None 7 0 LOAD_CONST 1 (0) 2 LOAD_CONST 1 (0) 4 COMPARE_OP 2 (==) 6 POP_JUMP_IF_FALSE 16 8 8 LOAD_CONST 1 (0) 10 LOAD_CONST 1 (0) 12 COMPARE_OP 8 (is) 14 RETURN_VALUE 9 >> 16 LOAD_CONST 2 (False) 18 RETURN_VALUE None
(Sep-25-2022, 04:37 PM)deanhystad Wrote: [ -> ]When I run 0==0 is 0, my Python complains about using "is" with a literal.

Same with both snippets 0 == 0 is 0 and 0 == 0 and 0 is 0

>>> (0 == 0) and (0 is 0)
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
True
>>> 0 == 0 is 0
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
True
I don't understand why the tortured bytecode for 0==0 is 0, but the reason for warning about 0 is 0 is because of this.

file junk.py
x = 0
file test.py
import junk
x = 0
print(x==junk.x is x)
When I run test.py
Output:
True
But if I change the value from 0 to 257
Output:
False
And if change the value to 256
Output:
True
The reason for this very odd behavior is Python reuses int objects for common int literals (0 to 256). Int literals outside this range are created on a as needed basis. So if x and y are the same int value, x == y is True, but x is y depends on the value of x.

And to make things more confusing, Python reuses literals when loading your module.
x=257
y=257
print(x==y is x)
Output:
True
Even though x and y are outside the "magic" range, Python only created one int object that is assigned to both x and y. Since 257 is an int, and int objects are immutable, there is no reason to create two int objects. When Python is converting the module to bytecodes it sees that 257 is used twice, and decides to save some space and some time and reuse the 257 int object. This optimization is not possible when the same int constant is used in two different modules (thus the reason for junk.py in my example).

So while 0==0 is 0 is an interesting bit of trivia, it really doesn't matter what value it returns. You should never use code that looks like this.