Question

Python object creation behavior

Coming from Java, I have trouble understanding how objects are allocated in Python. Consider this Python script:

x = ("a", "b")
y = ("a", "b")
print(x is y)  # True

x = ["a", "b"]
y = ["a", "b"]
print(x is y)  # False

In languages similar to Java, new keyword assures that a new instance with another memory location is created. But I'm assuming that's not the case here. So how can this behavior be explained? Is there any kind of pool for immutable types in Python to prevent duplicate literals?

I guess that Python keeps track of immutable types since the same case returns True for strings, complex numbers and other immutable objects. But if so, why bother?

 5  158  5
1 Jan 1970

Solution

 3

When determining whether two objects are equal, there are two methods:

  1. The is keyword determines whether two objects have the same memory id. In other words, a is b means that both a and b are stored in the same memory location with the same id. Basically a is b is equal to the expression id(a) == id(b).
  2. The == expression part is a comparison operator. It checks if two objects have the same value, but not necessarily stored in the same memory location.

When creating objects, Python gets to decide if the new object is to share the memory location (id) of an existing object. For example,

a = 1
b = 1
print(a == b) # always True
print(a is b) # most likely True

The results are stated in the comments. a is b in this case are probably True, while a == b is always True. But in this code,

a = [{'a':1, 'b':2, 'c':3}, ([1, 2, 3], 7, 8, 9), 'foo', ('bar', 'eggs')]
b = [{'a':1, 'b':2, 'c':3}, ([1, 2, 3], 7, 8, 9), 'foo', ('bar', 'eggs')]
print(a == b) # always True
print(a is b) # most likely False

a is b is probably False while a == b is still always True. This difference is because of how Python determines what storage is most efficient. In the first code, the two variables a and b are both assigned to a number 1. Because the number 1 is used too frequently, and it is an immutable object (remember ints are immutable objects), Python most likely will decide to store the object in the same memory location as the other 1s in this or other programs. However,

[{'a':1, 'b':2, 'c':3}, ([1, 2, 3], 7, 8, 9), 'foo', ('bar', 'eggs')]

is not too common, and the object is mutable (lists are mutable). Thus, in this case, Python will store the object in another memory location, so is might not return True, although both objects have the same value. An extremely important thing to note here is that all mutable objects that are created independently without any reference to other objects will always have a different memory location, regardless of the actual value. Note that tuples are a special case: they are both collections but also immutable, like strings. The following three codes will demonstrate the concept clearly:

# Program 1: Common and uncommon Immutable types
# Part I: Common
# define variables
a = 1
b = 1
c = 1
# check values
print(a == b) # True
print(a == c) # True
print(b == c) # True
# preform operation
print(a+b+c == a+b+c) # True
# use `is` to check values
print(a is b) # True
print(a is c) # True
print(b is c) # True
# You might expect `False` here, but keep in mind that `3` is another common integer which is immutable
print(a+b+c is a+b+c) # True

# Part II: Uncommon
# define variables
a2 = 123457997542
b2 = 123457997542
c2 = 123457997542
# check values
print(a2 == b2) # True
print(a2 == c2) # True
print(b2 == c2) # True
# preform operation
print(a2+b2+c2 == a2+b2+c2) # True
# use `is` to check values
print(a2 is b2) # True
print(a2 is c2) # True
print(b2 is c2) # True
print(a2+b2+c2 is a2+b2+c2) # False, because 740747985252 is not a common enough number that exists all the time

# Part III: tuples

print('entering tuples part')
t1 = (1, 2)
t2 = (1, 2)
t3 = (1, 2)

# check values
print(t1 == t2)  # True
print(t1 == t3)  # True
print(t2 == t3)  # True
# preform operation
print(t1 + t2 + t3 == t1 + t2 + t3)  # True

# Use `is`
print(t1 is t2)  # True
print(t1 is t3)  # True
print(t2 is t3)  # True
# preform operation
print(t1 + t2 + t3 is t1 + t2 + t3)  # False, as expected

# Program 2: Common and uncommon mutable objects
# Part I: common: lists
# define variables
a = [1, 2]
b = [1, 2]
c = [1, 2]

# check values
print(a == b)  # True
print(a == c)  # True
print(b == c)  # True
# preform operation
print(a + b + c == a + b + c)  # True

# Use `is`
print(a is b)  # False
print(a is c)  # False
print(b is c)  # False
# preform operation
print(a + b + c is a + b + c)  # False, as expected

# Part II: Uncommon
a2 = [{'a': 1, 'b': 2, 'c': 3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a': 1, 'b': 2}, (1, 2)), 'foo',
      'bar']  # extremely weird and uncommon object; as expected, all `is` operation will be `False`
b2 = [{'a': 1, 'b': 2, 'c': 3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a': 1, 'b': 2}, (1, 2)), 'foo', 'bar']
c2 = [{'a': 1, 'b': 2, 'c': 3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a': 1, 'b': 2}, (1, 2)), 'foo', 'bar']
# check values
print(a2 == b2)  # True
print(a2 == c2)  # True
print(b2 == c2)  # True
# preform operation
print(a2 + b2 + c2 == a2 + b2 + c2)  # True

# Use `is`
print(a2 is b2)  # False
print(a2 is c2)  # False
print(b2 is c2)  # False
# preform operation
print(a2 + b2 + c2 is a2 + b2 + c2)  # False, as expected

# Program 3: Reference pointer; sometimes obscure and hard to understand
# Part I: Common immutable
# Part 1: common integers
# define variables
a = 1
b = a
c = b

# use `is` to check values
print(a is b) # True
print(a is c) # True
print(b is c) # True
print(a+b+c is a+b+c) # True

# Part 2: Uncommon integers
# define variables
a2 = 123457997542
b2 = a2
c2 = b2

# use `is` to check values
print(a2 is b2) # True
print(a2 is c2) # True
print(b2 is c2) # True
print(a2+b2+c2 is a2+b2+c2) # False, because 740747985252 is not a common enough number that exists all the time

del a, b, c, a2, b2, c2 # delete variables
# ===============================================================================================================
# Part II: Mutable and tuples
#     Part 1: common: lists
# define variables
a = [1,2]
b = a
c = b

# Use `is`
print(a is b) # True
print(a is c) # True
print(b is c) # True
# preform operation
print(a+b+c is a+b+c) # False, as expected

del a, b, c
# Part 2: common: tuples
# define variables
print('entering tuples part')
a = (1, 2)
b = a
c = b

# Use `is`
print(a is b) # True
print(a is c) # True
print(b is c) # True
# preform operation
print(a+b+c is a+b+c) # False, as expected

# Note: the results of tuples are the same as with lists.

print('uncommon')
# Part III: Uncommon
a2 = [{'a':1, 'b':2, 'c':3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a':1, 'b':2}, (1, 2)), 'foo', 'bar'] # extremely weird and uncommon object; as expected, all `is` operation will be `False`
b2 = a2
c2 = b2

# Use `is`
print(a2 is b2) # True
print(a2 is c2) # True
print(b2 is c2) # True
# preform operation
print(a2+b2+c2 is a2+b2+c2) # False, as expected

# Notice all `is` results are `True` here no matter common or uncommon except for the last operation one
# ================================================================================================================
# Part III: List elements
# Part I: literal lists does not affect elements
l = [1, 2, 3]
l2 = l
l3 = [1, 2, 3]
print(l is l2) # True, same memory location
print(l[0] is l2[0]) # True, how the elements of a list or other collection are stored are not affected, same as in an independant variable
print(l[0] is l3[0]) # Still True
del l, l2

# Part II: elements cannot be changed by changing a copy of a element
l = [1, 2, 3]
l2 = l[0]
print(l, l2)
l2 = 1
print(l, l2)

del l, l2
# Part IV: Slice also cannot change the list
l = [1, 2, 3]
l2 = l[1:]
print(l, l2)
l2 = 4
print(l, l2)

del l, l2

# Part V: But, mutable objects within a list *can* be change by changing a copy of the object *IF* using append or other methods
l = [[1, 2, 3], [4, 5, 6]]
l2 = l[0]
l3 = l[0]
print(l, l2)
l2 = [7, 8, 9]
print(l, l2)

l3.append(10)
print(l, l3) # l changes

Although the code is a bit long, I hope you can understand Python's memory model better.


Note for code comments: the "Part" followed by roman numerals are the big categories, and the "Part" followed by Arabic numerals are the sub categories under the nearest big category.


Note that in the second program the tuples have a different behavior than lists. The is check return True, because in Python 3 tuples with the same value are usually stored in the same memory address location. In the third program, the result are the same as lists.

Quick Tip:

To get the reference count of a certain object, use sys.getrefcount():

>>> sys.getrefcount(1)
1796
>>> sys.getrefcount(9)
139
>>> sys.getrefcount([1,2])
1
>>> sys.getrefcount(2)
1131
>>> sys.getrefcount(200)
26
>>> sys.getrefcount(2002)
2
>>> sys.getrefcount([])
1
>>> sys.getrefcount({})
1
>>> sys.getrefcount(())
25760
>>> sys.getrefcount(True)
2356
>>> sys.getrefcount(False)
2943
>>> sys.getrefcount(list)
86

Note that even empty lists ([]) and dictionaries ({}) does not have any reference to it. However, empty tuples (()) have a large amount of references. The objects True and False both have a large amount of references.

2024-07-14
Luke L

Solution

 0

In your example both results are False, I use python version 3.11.0.

The is keyword is used to test if two variables refer to the same object id() in memory. Use the == operator to test if two variables values are equal.

x = ("a", "b")
y = ("a", "b")
print(x is y) # False, the test is äquivalent to id(x) == id(y).

x = ["a", "b"]
y = ["a", "b"]
print(x is y) # False

y = x
print(x is y) # True

# because now

print("object id:", id(x), id(y)) # same objectID is referenced now.

Another example:

a=1
b=1
print(a == b)
print(a is b) # not valid for all int(), see e,f
print(id(a), id(b))

c = (1,)
d = (1,)
print(c == d)
print(c is d)
print(id(c), id(d))

e = 1098765432
f = 1098765432
print(e == f)
print(e is f)
print( id(e),id(f))

Output:

True
True
4871700528 4871700528
True
False
4930976112 4930089312
True
False
4920911984 4920913552

PS: A very good explanation is found here by Luciano Ramalho | October 15, 2014.

2024-07-13
Hermann12