Immutable vs Mutable Types

I'm confused on what an immutable type is. I know the float object is considered to be immutable, with this type of example from my book:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

Is this considered to be immutable because of the class structure / hierarchy?, meaning float is at the top of the class and is its own method call. Similar to this type of example (even though my book says dict is mutable):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Whereas something mutable has methods inside the class, with this type of example:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

Also, for the last class(SortedKeyDict_a), if I pass this type of set to it:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

without calling the example method, it returns a dictionary. The SortedKeyDict with __new__ flags it as an error. I tried passing integers to the RoundFloat class with __new__ and it flagged no errors.

1

17 Answers

What? Floats are immutable? But can't I do

x = 5.0
x += 7.0
print x # 12.0

Doesn't that "mut" x?

Well you agree strings are immutable right? But you can do the same thing.

s = 'foo'
s += 'bar'
print s # foobar

The value of the variable changes, but it changes by changing what the variable refers to. A mutable type can change that way, and it can also change "in place".

Here is the difference.

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

Concrete examples

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]
11

You have to understand that Python represents all its data as objects. Some of these objects like lists and dictionaries are mutable, meaning you can change their content without changing their identity. Other objects like integers, floats, strings and tuples are objects that can not be changed. An easy way to understand that is if you have a look at an objects ID.

Below you see a string that is immutable. You can not change its content. It will raise a TypeError if you try to change it. Also, if we assign new content, a new object is created instead of the contents being modified.

>>> s = "abc"
>>> id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>> id(s)
4800100
>>> s += "uvw"
>>> id(s)
4800500

You can do that with a list and it will not change the objects identity

>>> i = [1,2,3]
>>> id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

To read more about Python's data model you could have a look at the Python language reference:

1

Common immutable type:

  1. numbers: int(), float(), complex()
  2. immutable sequences: str(), tuple(), frozenset(), bytes()

Common mutable type (almost everything else):

  1. mutable sequences: list(), bytearray()
  2. set type: set()
  3. mapping type: dict()
  4. classes, class instances
  5. etc.

One trick to quickly test if a type is mutable or not, is to use id() built-in function.

Examples, using on integer,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

using on list,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)
3

First of all, whether a class has methods or what it's class structure is has nothing to do with mutability.

ints and floats are immutable. If I do

a = 1
a += 5

It points the name a at a 1 somewhere in memory on the first line. On the second line, it looks up that 1, adds 5, gets 6, then points a at that 6 in memory -- it didn't change the 1 to a 6 in any way. The same logic applies to the following examples, using other immutable types:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

For mutable types, I can do thing that actallly change the value where it's stored in memory. With:

d = [1, 2, 3]

I've created a list of the locations of 1, 2, and 3 in memory. If I then do

e = d

I just point e to the same list d points at. I can then do:

e += [4, 5]

And the list that both e and d points at will be updated to also have the locations of 4 and 5 in memory.

If I go back to an immutable type and do that with a tuple:

f = (1, 2, 3)
g = f
g += (4, 5)

Then f still only points to the original tuple -- you've pointed g at an entirely new tuple.

Now, with your example of

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Where you pass

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(which is a tuple of tuples) as val, you're getting an error because tuples don't have a .clear() method -- you'd have to pass dict(d) as val for it to work, in which case you'll get an empty SortedKeyDict as a result.

1

If you're coming to Python from another language (except one that's a lot like Python, like Ruby), and insist on understanding it in terms of that other language, here's where people usually get confused:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

In Python, assignment is not mutation in Python.

In C++, if you write a = 2, you're calling a.operator=(2), which will mutate the object stored in a. (And if there was no object stored in a, that's an error.)

In Python, a = 2 does nothing to whatever was stored in a; it just means that 2 is now stored in a instead. (And if there was no object stored in a, that's fine.)


Ultimately, this is part of an even deeper distinction.

A variable in a language like C++ is a typed location in memory. If a is an int, that means it's 4 bytes somewhere that the compiler knows is supposed to be interpreted as an int. So, when you do a = 2, it changes what's stored in those 4 bytes of memory from 0, 0, 0, 1 to 0, 0, 0, 2. If there's another int variable somewhere else, it has its own 4 bytes.

A variable in a language like Python is a name for an object that has a life of its own. There's an object for the number 1, and another object for the number 2. And a isn't 4 bytes of memory that are represented as an int, it's just a name that points at the 1 object. It doesn't make sense for a = 2 to turn the number 1 into the number 2 (that would give any Python programmer way too much power to change the fundamental workings of the universe); what it does instead is just make a forget the 1 object and point at the 2 object instead.


So, if assignment isn't a mutation, what is a mutation?

  • Calling a method that's documented to mutate, like a.append(b). (Note that these methods almost always return None). Immutable types do not have any such methods, mutable types usually do.
  • Assigning to a part of the object, like a.spam = b or a[0] = b. Immutable types do not allow assignment to attributes or elements, mutable types usually allow one or the other.
  • Sometimes using augmented assignment, like a += b, sometimes not. Mutable types usually mutate the value; immutable types never do, and give you a copy instead (they calculate a + b, then assign the result to a).

But if assignment isn't mutation, how is assigning to part of the object mutation? That's where it gets tricky. a[0] = b does not mutate a[0] (again, unlike C++), but it does mutate a (unlike C++, except indirectly).

All of this is why it's probably better not to try to put Python's semantics in terms of a language you're used to, and instead learn Python's semantics on their own terms.

1

Difference between Mutable and Immutable objects

Definitions

Mutable object: Object that can be changed after creating it.
Immutable object: Object that cannot be changed after creating it.

In Python if you change the value of the immutable object it will create a new object.

Chloe Bennett

Chloe Bennett

Culture, Media & Entertainment Columnist

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.