Mutable and immutable types

Python has two kinds of built-in or user-defined types.

Mutable types are those that allow in-place modification of the content. Typicalmutables are lists and dictionaries: All lists have mutating methods, likelist.append() or list.pop(), and can be modified in place.The same goes for dictionaries.

Immutable types provide no method for changing their content. For instance, thevariable x set to the integer 6 has no “increment” method. If you want tocompute x + 1, you have to create another integer and give it a name.

  1. my_list = [1, 2, 3]
  2. my_list[0] = 4
  3. print my_list # [4, 2, 3] <- The same list has changed
  4.  
  5. x = 6
  6. x = x + 1 # The new x is another object

One consequence of this difference in behavior is that mutabletypes are not “stable”, and therefore cannot be used as dictionarykeys.

Using properly mutable types for things that are mutable in natureand immutable types for things that are fixed in naturehelps to clarify the intent of the code.

For example, the immutable equivalent of a list is the tuple, createdwith (1, 2). This tuple is a pair that cannot be changed in-place,and can be used as a key for a dictionary.

One peculiarity of Python that can surprise beginners is thatstrings are immutable. This means that when constructing a string fromits parts, appending each part to the string is inefficient becausethe entirety of the string is copied on each append.Instead, it is much more efficient to accumulate the parts in a list,which is mutable, and then glue (join) the parts together when thefull string is needed. List comprehensions are usually the fastest andmost idiomatic way to do this.

Bad

  1. # create a concatenated string from 0 to 19 (e.g. "012..1819")
  2. nums = ""
  3. for n in range(20):
  4. nums += str(n) # slow and inefficient
  5. print nums

Better

  1. # create a concatenated string from 0 to 19 (e.g. "012..1819")
  2. nums = []
  3. for n in range(20):
  4. nums.append(str(n))
  5. print "".join(nums) # much more efficient

Best

  1. # create a concatenated string from 0 to 19 (e.g. "012..1819")
  2. nums = [str(n) for n in range(20)]
  3. print "".join(nums)

One final thing to mention about strings is that using join() is not alwaysbest. In the instances where you are creating a new string from a pre-determinednumber of strings, using the addition operator is actually faster, but in caseslike above or in cases where you are adding to an existing string, usingjoin() should be your preferred method.

  1. foo = 'foo'
  2. bar = 'bar'
  3.  
  4. foobar = foo + bar # This is good
  5. foo += 'ooo' # This is bad, instead you should do:
  6. foo = ''.join([foo, 'ooo'])

Note

You can also use the % formatting operatorto concatenate a pre-determined number of strings besides str.join()and +. However, PEP 3101 discourages the usage of the % operatorin favor of the str.format() method.

  1. foo = 'foo'
  2. bar = 'bar'
  3.  
  4. foobar = '%s%s' % (foo, bar) # It is OK
  5. foobar = '{0}{1}'.format(foo, bar) # It is better
  6. foobar = '{foo}{bar}'.format(foo=foo, bar=bar) # It is best