python mutable objects vs immutable objects

Mutable/immutable object definitions

immutable object

The value in the memory pointed to by this object cannot be changed. When changing a variable, because the value it refers to cannot be changed, it is equivalent to copying the original value and then changing it, which will open up a new address, and the variable will point to this new address.

mutable object

The value in the memory pointed to by this object can be changed. After the variable (accurately speaking, the reference) changes, the value it refers to changes directly, and there is no copying behavior, and no new outgoing address is opened .

In Python, numeric types ( intand float), strings str, and tuples tuple are all immutable types. The list list, dictionary dict, and collection set are mutable types.

Examples of Immutable Objects

The first point to explain is is to judge whether the id of the two objects is the same, and the  == judgment is whether the content is the same.

a = 2
b = 2
c = a + 0
c += 0

print(id(a), id(b), id(2)) # id is the same
print(c is b) #True

Look at the string again

astr = 'good'
bstr = 'good'
cstr = astr + ''
print(cstr is bstr) # True
print(id(astr), id(bstr), id('good')) # The three ids are the same

 Same result as numeric types. If this is the case, after the variable is modified, it is notgood

astr = 'good'
print (id (astr))
astr += 'aa'
print(id(astr)) # id is not the same as above

 Since it is an immutable object, the value of the memory corresponding to the variable is not allowed to be changed. When the variable is to be changed, it actually copies the original value and then changes it, opens up a new address, and astr points to this new address (so the ids of the before and after asstr ​​are different). Any more objects pointing to it will be garbage collected. The same is true for int and float types.

Let's look at tuple again.

add = (1, 2, 3)
aee = (1, 2, 3)
print(id(add), id(aee), id((1, 2, 3))) # id varies

aee = (1, 2, 3)
print (id (aee))
aee += () # add empty tuple
print(id(aee)) # id changed!
print (aee) # (1 , 2,3

 Although it seems that (1 ,2, 3)it should be consistent with the above. Is this a mutable object? look again

add = (1, 2, 3)
aee = add
print(id(aee), id(add)) # These two ids are the same
aee += (4, 5, 6)
print(id(aee)) # aee's id has changed!
print(add) # add is still (1, 2, 3) unchanged

It is consistent with the numeric type and str type again. In the case of mutable objects add = aee, they point to the same address (same id) for sure. But not different references to the same object, because if so, a change in aee would cause a change in add, which is not the case in a tuple. So tuple is an immutable object, but it is slightly different from str and numeric types. Usually, the immutability of a tuple means that the value stored in it cannot be changed (in some special cases, such as a list stored in a tuple, the elements in the list can be changed. But in fact, the tuple has not been changed ).

For str、int、floatas long as they are of the same type and the values ​​are the same, then their ids are the same. (Why do you say the types are the same?)

a = 2.0
b = 2
print(a is b) # False, one int and one float, different types

 2 and 2.0 are not on the same address.

Mutable Object Example

lis = [1, 2, 3]
lis2 = [1, 2, 3]
# Although their contents are the same, they point to different memory addresses
print(lis is lis2)
print(id(lis), id(lis2), id([1, 2, 3])) # all three ids are different

 In the case of assignment

alist = [1, 2, 3]
# alist is actually a reference to the object, blist = alist is the transfer of the reference, now both references point to the same object (address)
blist = alist
print(id(alist), id(blist)) # id is the same
# So one change will affect the other
blist.append(4)
print(alist) # Change blist, alist also becomes [1 ,2 ,3 4]
print(id(alist), id(blist)) # id is the same as the id when the above value has not changed 

blist = alistthis sentence. alistIt's actually a reference to an object, blist = alisti.e. pass by reference, now both references point to the same object (address). So one change will affect the other.

look at the set again

abb = {1, 2, 3}
acc = fig
print(id(abb), id(acc))
acc.add(4)
print(abb) # {1, 2, 3, 4}
print(id(abb), id(acc)) # equal 

It is the same as the example in the list above.

Since the object to which the variable refers can be modified, it does not need to be copied and then changed, but is directly changed in place, so no new memory will be opened, and the id will remain unchanged before and after the change.

Of course, this is not the case with immutable objects, you can compare it with this

abc = 3
dd = abc
dd = 43
print(abc) # 3, does not change with the change of dd

 But if it is a copy, it is just copying the content, and it is not passed a reference. This is especially useful when you want to use the values ​​of the list without modifying the original list.

blist = alist[:]  # or alist.copy()
print(alist is blist)  # False
blist.append(4)
print(alist) # still [1, 2, 3] no change

as function parameter

test_list = [1, 2, 3, 4]
test_str = 'HAHA'

def change(alist):
    alist.append(5)

def not_change(astr):
    astr.lower()

change(test_list)
not_change(test_str)
print(test_list) # changed the original value
print(test_str) # no change

 Of course, if you don't want to change the value of the original list, the parameter can be passed in a copy of the column variable.alsit[:]

interesting example

Looking at an interesting example, we know that list can be used to +add a list.

a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
# Actually a2 points to the new object, the id has changed.
# So now a2, a1 are not two references to the same object, a2 changes a1 will not change
a2 = a2 + [4] # In this equation, the a2 on the right is still the same as the id of a1. Once the assignment is successful, a2 points to the new object
print(id(1), id(a2)) # Not equal, the id of a2 has changed
print(a1) # [1, 2, 3] does not change

 If so write

a1 = [1, 2, 3]
a2 = a1
print(id(a1), id(a2))
a2 += [4] # Equivalent to calling a2.extend([4]), no new objects are generated for in-situ changes
print(id(1), id(a2)) # equal, the id of a2 has not changed
print(a1)

 The difference is that a2 += [4]this sentence is equivalent to calling a2.extend([4])and changing in place, and no new objects are generated. 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324605881&siteId=291194637