SoFunction
Updated on 2024-11-16

python mutable objects, immutable objects explained

When writing python programs, there is a lack of understanding of mutable and immutable objects here, which leads to always making some detailed mistakes. Take the following program as an example:

ab = {'a':1, 'b':2}
list1 = []
for i in range(2,5):
    ab['a'] = i
    (ab)
print(list1)     # [{'a': 4, 'b': 2}, {'a': 4, 'b': 2}, {'a': 4, 'b': 2}]

This code would have expected the result to be [{'a': 2, 'b': 2}, {'a': 3, 'b': 2}, {'a': 4, 'b': 2}] but The value of key a in each dictionary in the list becomes the last value 4. This brings us to variable and immutable objects in python.

First of all, what is an object?

In python, everything is an object, and objects must have three properties: address, type, and value.

When a=5, it is actually a creation and reference process. First an object 5 is created,5 is present in memory with its own separate piece of address space, and then a points (references) to 5.

Mutable vs. immutable objects

When the value of an object changes, but the memory address does not change, it is a mutable type

When the value of an object changes and the memory address changes, it is an immutable type

As you know, the mutable objects in python are: lists, dictionaries, collections

Immutable objects are: tuples, strings, values

The following code explains mutable and immutable objects better:

When python references an immutable object, it looks to see if the object has been created, and if it has, the variable references the object directly without requesting new memory space.

a = 5
b = 5
# At this point, a and b are both referencing object 5, so the address is the same #
print(id(a), id(b))         # 1662825664 1662825664
# The object has changed, a changed reference, and the address has changed #
a = 6
print(id(a), id(b))     # 1662825696 1662825664

When referencing a mutable object, a new memory address is created, and when the value of the mutable object changes, the original memory address does not change

list1 = [1,2,3,4]
list2 = [1,2,3,4]
print(id(list1), id(list2))   #1754039591880  1754040417288
(5)
print(id(list1), id(list2))   #1754039591880  1754040417288

Note: If you directly set list2 = list1, then list1 and list2 will have the same address. It's just a different name.

list1 = [1,2,3,4]
list2 = list1
print(id(list1), id(list2))   #2272617112520 2272617112520
(5)
print(id(list1), id(list2))   # 2272617112520 2272617112520

So why are lists mutable and strings or numeric types immutable? This goes deep into the underlying implementation of python datatypes.

List Bottom

List implements the storage of list elements by referencing arrays.

Simply put, a contiguous address space is carved out of the list to store the addresses of referenced elements. So the list stores addresses, not specific values.

在这里插入图片描述

dictionary underlay

Storing and Accessing Values with Sparse Arrays

1. Dictionary creation process

  • Create a hash table (sparse array that can be dynamically expanded)
  • Calculate the hash value of a key via hash()
  • Determine its position in the hash table based on the calculated hash value
  • Deposit the value at this location

2. Dictionary access process

  • Calculate the hash value of the key to be accessed
  • Based on the calculated hash value, its position in the hash table is determined according to certain rules
  • Retrieve the value stored at this location

string primer

pass (a bill or inspection etc)compact arrayImplementing String Storage

String data is stored contiguously in memory and is space efficient. Therefore, strings are immutable types.

The reason: the size of each character is fixed, so the size of a string is also fixed, and a fixed size of space can be allocated to the string.

Add some more.On passing parameters to functionsexclusionary rule

value transmission

When the main function passes a parameter of immutable type to the calling function, it actually just passes a copy of the real parameter (i.e., a temporary copy) to the called function, not the real parameter itself, so that the called function can't directly modify the value of the variable in the main function but can only modify the value of the temporary copy that is private to it.

pass by reference

When the main function passes an argument of variable type to the calling function, it actually passes a reference to the real parameter into the calling function, and operating on the reference is equivalent to operating on the object it specifies.

Note the following two scenarios:

list1 = [1,2,3,4]
def solution(list1):
    list1 = [1,2,3,4,5]
    return list1
solution(list1)
print(list1)      # [1,2,3,4]
list1 = [1,2,3,4]
def solution(list1):
    (5)
    return list1
solution(list1)
print(list1)      # [1,2,3,4,5]

In the first case, the use of "=" inside the function is actually equivalent to creating a new block of memory to hold the new object and pointing list1 to the new object, so it doesn't change list1 globally.

The second, using append, means that the value of the original object is changed, so it is still an operation on the original object.

Reference:

Python Classes, Objects, Data Classification, and Understanding Function Parameter Passing

An article that teaches you the underlying implementation of python datatypes

summarize

That's all for this post, I hope it helped you and I hope you'll check back for more from me!