When determining whether two objects are equal, there are two methods:
- 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)
.
- 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 int
s are immutable objects), Python most likely will decide to store the object in the same memory location as the other 1
s 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 (list
s 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 tuple
s 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.