Reputation: 21
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
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
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