Fork me on GitHub

Python深浅拷贝

Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的 。本文中重点讲解下Python中的深浅拷贝知识点

  • 内存相关
  • 浅拷贝
  • 深拷贝

内存相关

赋值和修改内存地址中的数据

查看内存地址id()函数

  • 实例一

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 实例1
    a1 = [1,2,3]
    a2 = [1,2,3]

    v1 = 666
    v2 = 666

    v1 = "abcd"
    v2 = "abcd"

    print(id(a1), id(a2))
    print(id(v1), id(v2))
    print(id(v3), id(v4))
    # 内存地址:结果本应该是不同的,由于Python中小数据池的缓存机制,使得某些情况下内存地址相同
    2072273207688 2072273971080
    2072273946608 2072273945584
    2072274079000 2072274079000
  • 实例二

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 实例2
    v5 = [1,3,4,5]
    print(id(v5))
    v5 = [1,3,4]
    print(id(v5))

    # 开辟新的内存,存放不同的数据
    2072291579016
    2072291262600
  • 实例三

    1
    2
    3
    4
    5
    6
    7
    8
    9
    v6 = [1,3,4,8]
    v7 = v6
    v6.append(888)
    print(v7) # [1, 3, 4, 8, 888]

    v6 = [1,3,4,8]
    v7 = v6 # v7和v6指向同一个内存地址
    v6 = [1,3,888] # 开辟了新的内存地址,存放数据;v7是不会变的
    print(v7) # [1,3,4,8]
  • 实例四

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    v1 = [1,2]
    v2 = [3,4,v]

    # 练习题1
    v1.append(99)
    print(v2) # [3,4,[1,2]]

    # 练习题2
    v2[2].append(999)
    print(v1) # [1,2,999]

    # 练习题3
    v1 = 888
    print(v2) # [3,4,[1,2]]

    # 练习题4
    v2[2] = 666
    print(v1) # [1,2]

    结论

  • 赋值:凡是赋值都不影响

  • 修改内存地址:只有修改内存中数据才会影响

小数据池机制

整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。 Python认为其内存是不变的,做了缓存。列表、元组、集合、字典等不会做缓存

数字:-5~256

字符串:“abcd”,出现特殊字符则内存地址不同

v1和v2的内存地址理应不同,但是由于小数据池机制,变得相同;a1和a2同理。

v3和v4:在系统中重新开辟了内存

== 和is区别

== :比较值是否相等

is:判断内存地址是否相同

浅拷贝copy

不管是浅拷贝还是深拷贝,都会开辟新的内存

浅拷贝只拷贝第一层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#  浅拷贝 

# 单层列表
V1 = [1, 2, 3, 4]
V2 = copy.copy(V1) # 相当于是开辟新的内存来存储v2
print(id(V1), id(V2)) # 2438370903176 2438359041544
print(V1 == V2) # T
print(V1 is V2) # F 内存地址不同
print(V1[0] is V2[0]) # T

# 嵌套列表
V1 = [1, 2, [3, 4]]
V2 = copy.copy(V1)
print(id(V1), id(V2))
print(id(V1[2]), id(V2[2]))

2438359083336 2438359084168 # 开辟新内存
2438359084104 2438359084104 # 最里层的内存不会变化,只拷贝最外层

深拷贝deepcopy

拷贝所有的可变类型数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 深拷贝

# 单层
V1 = [1, 2, 3, 4]
V2 = copy.deepcopy(V1)
print(id(V1), id(V2)) # 2522354523272 2522354651656 拷贝最外层
print(V1 == V2) # T
print(V1 is V2) # F 内存地址不同
print(V1[0] is V2[0]) # T,里面的123是int型,由于小数据池机制,内存地址相同


# 嵌套
V1 = [1, 2, [3, 4]]
V2 = copy.deepcopy(V1)
print(id(V1), id(V2))
print(id(V1[2]), id(V2[2]))
print(V1 == V2) # T
print(V1 is V2) # F
print(V1[0] is V2[0]) # T 1是不可变类型,小数据池机制,内存相同
print(V1[2] == V2[2]) # T 值相等
print(V1[2] is V2[2]) # F 内存不同

2522354652296 2522354651272 # 所有的可变类型数据都会拷贝,所有最外层和最里层的列表都会拷贝
2522353804168 2522354652616


  • 浅拷贝:copy(),拷贝第一层
  • 深拷贝:deepcopy(),拷贝所有可变类型的数据;存在嵌套时,深浅拷贝才有区别

对于字符串str、整数型int、布尔值bool三种不可变的对象类型,深浅拷贝是一样的,直接在内存中直接开辟空间进行存储。

特殊情况

元组是不可变类型,当里面的元素全部是不可变类型时,深浅拷贝没有区别;只有当里面的元素由可变类型(比如列表时),才会有区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import copy
t1 = (1,2,3,4)
t2 = copy.copy(t1)
t3 = copy.deepcopy(t1)
print(id(t1), id(t2))
print(id(t1), id(t3))

2522364792088 2522364792088 # 元组是不可变类型,内存地址相同;深浅拷贝相同
2522364792088 2522364792088

# 元组中嵌套列表(可变元素)
t1 = (1,2,3,[4,5,6])
t2 = copy.copy(t1)
t3 = copy.deepcopy(t1)
print(id(t1), id(t2))
print(id(t1), id(t3))

2522361389368 2522361389368 # 元组是不可变类型,内存地址相同
2522361389368 2522361389928 # 深拷贝情况下不同

本文标题:Python深浅拷贝

发布时间:2019年10月14日 - 23:10

原始链接:http://www.renpeter.cn/2019/10/14/Python%E6%B7%B1%E6%B5%85%E6%8B%B7%E8%B4%9D.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea