Python Objects: Mutable vs. Immutable

The way I like to remember which types are mutable and which are not is that containers and user-defined types are generally mutable while everything else is immutable. Then I just remember the exceptions like tuple which is an immutable container and frozen set which is an immutable version of set (which makes sense, so you just have to remember tuple).

The following are immutable objects:

  • Numeric types: int, float, complex
  • string
  • tuple
  • frozen set
  • bytes

The following objects are mutable:

  • list
  • dict
  • set
  • byte array

WHEN MUTABILITY MATTERS

Mutability might seem like an innocuous topic, but when writing an efficient program it is essential to understand. For instance, the following code is a straightforward solution to concatenate a string together:

string_build = ""
for data in container:
    string_build += str(data)
print(string_build)

But in reality, it is extremely inefficient. Because strings are immutable, concatenating two strings together actually creates a third string which is a combination of the previous two. If you are iterating a lot and building a large string, you will waste a lot of memory creating and throwing away objects. Also, do not forget that nearing the end of the iteration you will be allocating and throwing away very large string objects which is even more costly.

The following is a more efficient and pythonic method:

builder_list = []
for data in container:
    builder_list.append(str(data))
"".join(builder_list)

### My favorite way is to use a list comprehension
### which is cleaner code and runs faster

"".join([str(data) for data in container])

This code takes advantage of the mutability of a single list object to gather your data together and then allocate a single result string to put your data in. That cuts down on the total number of objects allocated by almost half.

Another gotcha related to mutability is this little number:

def my_function(param=[]):
    param.append("thing")
    return param

What you might think would happen is that by giving an empty list as a default value to param, a new empty list is allocated each time the function is called and no list is passed in. But what actually happens is that because Python only evaluates functions definitions once and a list is a mutable object, every call that uses the default list will be using the same list

print(my_function())  # prints: ["thing"]
print(my_function())  # prints: ["thing", "thing"]

Do not put a mutable object as the default value of a function parameter. Immutable types are perfectly safe. If you want to get the intended effect, do this.

def my_function2(param=None):
    if param is None:
        param = []
    param.append("thing")
    return param

CONCLUSION

Mutability matters. Learn it. Primitive-like types are probably immutable. Container-like types are probably mutable.

Source: Python Objects: Mutable vs. Immutable

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s