Why does Python do this?

is is a test to see whether both variables refer to the same object.

When I do:

two = 2
too = 2
print (two is too)

I get True. Which is not surprising since Python likely points both variables to the same constant in memory.

But when I continue with:

x = 1 + 1
print (x is too)

I also get True.

That cannot be computationally efficient to search all objects for the value 2 just to reuse the object.

I highly doubt that.

The is operator is implemented by a class. In your example, I suspect there is a built-in class for integers. The class on the left decides what to return. The built-in integer class very likely returns True if the other is also an integer class and the instance values are the same. From our perspective two and too appear to the same object instance but in reality are not.

In other words, I could create an Orange and Apple class that always return True for the is operator. It would be a silly thing to do but Python has the flexibility to do such a thing.

This example would tend to support that the is is saying they are both integer objects

Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "copyright", "credits" or "license()" for more information.
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print(x is y)
False
>>> two = 2
>>> too = 2
>>> print(two is too)
True
>>> x=1+1
>>> print(x is too)
True
>>> y = x+too
>>> print(y is two)
False
>>> 

Above using Python 3.x

one = 1
two = 2
print(one is two)

False

“1+1” probably gets optimized away to “2” by the parser since only constants are involved.

1 Like

Great point! Time for a more complex way to do it…

My apologies. I was wrong…

>>> two = 2
>>> too = 2
>>> print (two is too)
True
>>> three = 3
>>> print (two is three)
False
>>> id(too)
1749066880
>>> id(two)
1749066880
>>> plus = 1 + 1
>>> id(plus)
1749066880
>>> id(2*1)
1749066880
>>> id(three)
1749066912
>>>

They are the same object instance.

Hash table. Big-Oh of a constant.

1 Like

I have been doing this testing in Idle and have not been able to fool it. Time to try feeding a module to the interpreter directly.

It is such an obvious joke, but “It depends upon what the meaning of the word ‘is’ is.”

1 Like

It looks like Python caches values up to a certain size. I saw this which is interesting.

1 Like

The limit is somewhere between here and there…

a=2**16
print(a)
b=256*256
print(b)
print(a is b)

65536
65536
False

https://stackoverflow.com/questions/13650293/understanding-pythons-is-operator

“The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. x is not y yields the inverse truth value. [6]”

https://docs.python.org/2/reference/expressions.html#is

This next part explained a lot for me in the way of your question

is and is not are the two identity operators in Python. is operator does not compare the values of the variables, but compares the identities of the variables. Consider this:


>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

The above example shows you that the identity (can also be the memory address in Cpython) is different for both a and b (even though their values are the same). That is why when you say a is b it returns false due to the mismatch in the identities of both the operands. However when you say a == b, it returns true because the == operation only verifies if both the operands have the same value assigned to them.

From the stack overflow link

If you can, I would look at the memory links when you run your tests and see what is actually being pointed to when you execute it.

In your example the compiler is probably looking at your initialization of the the “two” and “too” variables and figuring

"well crap this guy statically assigned the same value (==) to both of these variables, might as well point to the same memory space while inside this code module, but when compiled with an expression (such as a=b+2, (which would be interesting to see if it used the same “ID” for

a=1
b=a+0
c=a+b 
d=2
z=a+b
id(a)
id(b)
id(c)
id(d)
id(z)

)) for the variable definition it doesn’t know what the value may be till it runs therefore it can’t link the variables to the same memory space".

This could have been done to save memory allocation due to lazy programmers who would use multiple statically assigned variables that all had the same value.

edits for formatting, hey looky here wrote my first little bit of python

a=2**8
print(a)
b=16*16
print(b)
print(a is b)

256
256
True

a=2**8+1
print(a)
b=16*16+1
print(b)
print(a is b)

257
257
False

Oh jeez, I’m having flashbacks to 1998

1 Like

So this got me thinking (a bad thing), why would you need/want to know if two variables were in fact the same object? Can’t think of an example where I ever did and wondering what situations this would be needed in.

And in the world down under…

a=-(2**2+1)
print(a)
b=-(2*2+1)
print(b)
print(a is b)

-5
-5
True

a=-(2**2+2)
print(a)
b=-(2*2+2)
print(b)
print(a is b)

-6
-6
False

It isn’t really needed for simple stuff but if you were using something more dynamic in the code for variables and were getting odd results it might help to know if your compiler was screwing you up by pointing both variables at the same memory id because it thought they were the same but in fact were not due to the way it interpreted the initialization of the variable

try this and for some reason they point to different places, and the compiler move pointers while its running if you redefine a var inside a loop

a=-10
b=1
while (a is not b):
   a=a+b
   print("a= ",a," ",id(a))
   print("b= ",b," ",id(b),"\n")
   

Consider this:

list=[1,2,3]
another=[1,2,3]
clone=list
print(list is another)
print(list is clone)

False
True

Assigning one variable to another makes both refer to the same object. By definition. is allows you to find out whether that is the case.

It looks as if the you may have to take into account how variable is defined, when you define a list,

list=[1,2,3]
another=[1,2,3]

part of that definition is the name of the list, which needs to be stored in memory as well so that it can be refrenced, so even if you have defined them as the same thing, the compiler has to be able to know which one you are refrencing
but when you assign

clone=list

you are telling the compiler to look at what ever it has stored in memory at the list location, so the clone list only needs a memory pointer (pointed at list) and name reference for it to be able to be used.

http://foobarnbaz.com/2012/07/08/understanding-python-variables/

and for bill who I guess knew this all along

The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :slight_smile:

And apparently you can use id() and addresssof() to abuse the memory allocation of the array of integers (-5 to 256) that python uses. Also it allows you to excute code outside code by inserting code into a function stored in memory

I did not know. Honest! I hacked it as you watched.

1 Like