5 Levels of Understanding the Mutability of Python Objects

Yang Zhou
Yang Zhou
Dec 8, 2020 · 5 min read

Introduction

Every Python programmer may have been confused about the mutability of objects. This concept is indeed a little difficult to understand in Python.

Unfortunately, questions related to the mutability of objects are the most popular questions in technical interviews for Python developer. Such as:

  • What is the difference between mutable and immutable objects?

The above questions are enough to distinguish between junior and senior Python developers.

To help you upgrade to a senior programmer, this article will explain the relative concepts through 5 levels from the appearance to the essence. After reading, answering the above questions will become very easy for you. 🎉

Level 0: Understand the Concepts of Mutable and Immutable Objects

Simply put, the mutable objects can be changed after creation. The immutable objects cannot be changed after creation. Let’s check it out by two examples:

An example of a mutable object

As the above example shown, if we changed the leaders list, its content will be changed but its identity won’t be changed. Because the list object in Python is mutable, we can do the in-place modifications on it.

An example of an immutable object

As stated above, since a string is an immutable object in Python, we can not change anything on it, even just capitalize its first letter. We must assign the result of Leader.capitalize() to a new string.

Level 1: Know the Mutability of Built-In Objects

Being familiar with the mutability of built-in objects is the fundamental ability for a Python programmer. The following is a brief checklist of it:

The mutability of Python built-in objects
The mutability of Python built-in objects
Mutability of Python built-in objects (made by Yang Zhou)

As demonstrated above, Python gives us lots of flexibility to use different types of objects. For example, we not only have the set, but also has its immutable version — fronzenset.

Level 2: Understand What Happens Under the Hood

Understanding the basic concepts is not enough for senior developers. We should be aware of what happens under the hood for totally knowing its mechanism.

In fact, everything in Python is an object and the assignment operation is just binding a name to an object. This ground truth is very significant to understand the following example:

The above example could be confusing:

The leaders is a mutable list, and we can change its value anyway.

However, why the id, which is the identity of the object, is changed after the last assignment? Why this operation does not get the original list?

If we totally understand the assignment operation of Python, the answer is simple and obvious:

The ["Mark Zuckerberg"] is another list object, and the name leaders will be bound to this object when we run leaders = ["Mark Zuckerberg"].

Due to the name leaders has already represented another object, it’s id is different.

Let’s see a more complex example to deepen our understanding:

As the above example shown, when B and A are bound to the same object, changing B will affect A as well. When B is bound to another object, changing B won’t affect A at all.

Level 3: Know How Objects are Passed to Functions

There is a classic Python interview question: How a Python function evaluates its arguments? Some answers seem like:

  • Mutable objects are call-by-reference and immutable objects are call-by-value.

To some extent, all the three answers are correct. But if we strictly state it, the third answer is the 100% correct answer. As explained on Wikipedia, the “call-by-sharing” is not as commonly used as “call-by-reference” and “call-by-value”, so it’s okay if we don’t use this special terminology.

If I am an interviewer, it doesn’t matter how the interviewee says the terminology. It does matter how he or she explains the reason.

Why immutable objects are not call-by-value?

As the following example code, when we change the value of argument val, the b won’t be changed. It acts like call-by-value.

But check out the following code:

The id of val is identical to the id of b. If the val argument is all-by-value, this is impossible.

How can we explain the above examples?

We should review the ground truth about Python objects and assignment operations again:

Everything in Python is an object and the assignment operation is just binding a name to an object.

Therefore, in the example2.py, the val is another name besides b which is bound to the integer object 5. The two names have identical ids since they are bound to the same object. If we change the integer, because of its immutability, a new integer object will be created and the val will be bound to it.

Why mutable objects are not call-by-reference?

We may think mutable objects are call-by-reference if we see an example like following:

As demonstrated above, appending a new value to the val will affect the L as well. It acts like call-by-reference.

Unfortunately, a counterexample can still be indicated here:

As the above example shown, the L didn’t change even if the val was changed. If it’s call-by-reference, this situation is hard to explain.

The ground truth mentioned previously can help us again:

For example 4, after the vals = [7,8,9] was executed, the name vals had been bound to another list object — [7,8,9]. Since the vals and L were bound to different list objects, the modification of vals would not affect L anymore.

For example 3, because of the mutability of the list object, the vals.append() method just executed an in-place change of the original list object. There was no other new objects. The vals and L were bound to the same object all the time, so the modification of vals would affect L.

Conclusion

The terminologies like call-by-value and call-by-reference are commonly used in C, C++ and other similar programming languages. However, values in Python are based on objects rather than primitive types, i.e., that all values are “boxed”. Due to this fact, using call-by-value or call-by-reference to describe Python’s parameter passing mechanism is not precise enough. The correct jargon is “call-by-sharing”.

Level 4: Be Aware of the Mutability of Nested Objects

Last but not least, we should know that the mutability of nested objects depends on each object itself. For instance:

As stated above, the tp is a tuple which is immutable, but the first element of tp is a list that is mutable. Therefore, we can modify the first element of tp even if it’s an immutable object.

Conclusion

The mutability of objects is one of the core concepts of Python. To totally understand it, we must know the ground truth of Python objects:

Everything in Python is an object and the assignment operation is just binding a name to an object.

Thanks for reading. If you like it, please follow me to enjoy more great Python tutorials.

TechToFreedom

Dive into technology, investment and entrepreneurship.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store