Python中所有类型的值都是对象,这些对象分为可变对象与不可变对象两种:

  • 不可变类型

    floatintstrtupleboolfrozensetbytes

    tuple自身不可变,但可能包含可变元素,如:([3, 4, 5], ‘tuple’)

  • 可变类型

    listdictsetbytearray自定义类型

+=操作符

+=操作符对应__iadd__魔法方法,对于不可变对象a+=ba=a+b等价,对于可变对象并不等价,dictset不支持+=和+操作符。

l1 = l2 = [1, 2, 3]# 只有l1发生变化# l1 = l1 + [4]# l1和l2都发生变化,输出[1, 2, 3, 4, 5]l1 += [4, 5]print(l1)print(l2)

浅拷贝 深拷贝

与赋值不同,拷贝(可能)会产生新的对象,可通过拷贝来避免不同对象间的相互影响。

在Python中,不可变对象,浅拷贝和深拷贝结果一样,都返回原对象:

import copy​​t1 = (1, 2, 3)t2 = copy.copy(t1)t3 = copy.deepcopy(t1)print(t1 is t2) # Trueprint(t1 is t3) # Trueprint(id(t1), id(t2), id(t3)) # 输出相同值

对于可变对象,则会产生新对象,只是若原对象中存在可变属性/字段,则浅拷贝产生的对象的属性/字段引用原对象的属性/字段,深拷贝产生的对象和原对象则完全独立:

l1 = [1, 2, 3]l2 = l1.copy()print(l1 is l2)  # Falsel2[0] = 100print(l1[0])  # 1
import copy​​class Id:    def __init__(self, name):        self.name = name​​class Person:    def __init__(self, id: Id):        self.id = id​​p1 = Person(Id("eason"))p2 = copy.copy(p1)print(p1 is p2)  # Falseprint(p1.id is p2.id)  # Truep2.id.name = "p2"print(p1.id.name)  # p2​p3 = copy.deepcopy(p1)print(p1 is p3)  # Falseprint(p1.id is p3.id)  # Falseprint(p1.id.name is p3.id.name)  # True,字符串不可变,这里name属性的地址一样p3.id.name = "p3"print(p1.id.name)  # 还是p2

Python中可使用以下几种方式进行浅拷贝:

  • 使用copy模块的copy方法

  • 可变类型切片

    l1 = [1, 2, 3]l2 = l1[:]print(l1 is l2)  # False
  • 可变类型的copy方法

    [].copy(){}.copy()set().copy()
  • 调用list, set, dict方法

    l1 = [1, 2, 3]l2 = list(l1)l2[0] = 100print(l1[0])  # 1

推荐阅读

Different behaviour for list.__iadd__ and list.__add__

学习Python一年,这次终于弄懂了浅拷贝和深拷贝

copy— Shallow and deep copy operations