Slaycapь
Slaycapь

Reputation: 21

Realization of mutable and immutable objects in python

I have encountered difficulties in understanding the realization of immutable and mutable objects. As I understand it, when trying to change a mutable object, such as an array, the interpreter simply modifies some defined memory cells that this array occupies. And when you try to change an immutable object, a separate memory area with a new object is allocated. But why not just write the new object to the same memory area where the old one was? What are the advantages of placing a new object at a new address?

Upvotes: 0

Views: 90

Answers (2)

ti7
ti7

Reputation: 18846

Python deals in names and references, not really variables, which can also be confusing for users used to "declaring" variables

Some great reading about the topic

When you create a new object, you can give it a name, or not bother

"hello"      # object which is the string "hello"
a = "hello"  # the name `a` now refers to a string "hello"
a = b = "hello"  # `a` and `b` now both refer to "hello"
a = "world"      # `b` is still "hello", but `a` refers to "world"
c                # raises `NameError` as there is no `c`

You actually see this frequently for docstrings

def foo():
    """ I'm a docstring, so I don't have a simple name in the function..
        however, outside I can be found at `foo.__doc__` !
    """

It's a simplification, but when you create a list (or other similar container), it then has all the references to its internals as named indicies

l = ['a', 'b', 'c']
l[0]  # a
l[1]  # b
l[2]  # c

If you change those references, you change where the names inside refer, but existing references won't be affected!

>>> l = ['a', 'b', 'c']
>>> x = l[1]
>>> l[1] = True
>>> l             # field 1 of l has been changed!
['a', True, 'c']
>>> x             # but the name x still refers to "b"
'b'

Deeper (if confusingly), you could make a list with a list, and then make a name to that index!

>>> l = [[]]
>>> x = l[0]
>>> x.append("test")
>>> l                 # both `l` and `x` refer to the inner list!
[['test']]
>>> x
['test']

As to whether some particular object is mutable or immutable, while a language feature, is frequently a convenience to do optimizations and assure hashes work - there are not many, so you just have to remember and experiment until you get a good understanding

>>> {set([1]): 2}  # a set can mutate, so dict can't use it as a key!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
>>> {frozenset([1]): 2}
{frozenset({1}): 2}

For more and generally as a great reference, check out the official datamodel docs https://docs.python.org/3/reference/datamodel.html it gets right into immutability and discusses all basic builtin types!

Upvotes: 3

matszwecja
matszwecja

Reputation: 8076

The old object still exists when you create a new one. This memory is not free for the new object to be put there, and only when you bind a variable name to new address, will this space be freed for a new object to be put there. Very simple code that demonstrates the issue (Note: it might not work as described, based on implementation details)

x = "foo"
a = x * 2
print(id(a)) # 2874144541504
a = x * 2
print(id(a)) # 2874144541552 
a = x * 2
print(id(a)) # 2874144541504

We can see that ids 1 and 3 are the same, but 2 is different. That's because when we do 2nd x * 2, memory at id 1 is not free yet. But, since we lose reference to the old value after assignment, it gets garbage collected and can be assigned again with 3rd assignment.

Upvotes: 1

Related Questions