1. print( 坑的信息 )

  • 挖坑时间:2019/01/10
  • 明细
坑的编码 内容
Py006-3 Python3 中的深拷贝与浅拷贝

2. 开始填坑

2.1 Python3.7 官方文档

2.2 赋值、切片与 copy()

# 例 1
lst1_1 = [0, 1, 2, 3, 4]
lst1_2 = lst1_1 # Python 的赋值语句不复制对象,而是在目标和对象之间建立联系
lst1_3 = lst1_1[:] # 切片
lst1_4 = lst1_1.copy() # copy() print("lst1_1 =", lst1_1)
print("lst1_2 =", lst1_2)
print("lst1_3 =", lst1_3)
print("lst1_4 =", lst1_4)
print('-'*30) print("id(lst1_1) =", id(lst1_1))
print("id(lst1_2) =", id(lst1_2))
print("id(lst1_3) =", id(lst1_3))
print("id(lst1_4) =", id(lst1_4))

>>>

lst1_1 = [0, 1, 2, 3, 4]
lst1_2 = [0, 1, 2, 3, 4]
lst1_3 = [0, 1, 2, 3, 4]
lst1_4 = [0, 1, 2, 3, 4]
------------------------------
id(lst1_1) = 2523757320136
id(lst1_2) = 2523757320136
id(lst1_3) = 2523757319624
id(lst1_4) = 2523757319816

分析

  • lst1_1 与 lst1_2 指向同一片内存地址,是同一事物的两个名字
  • 切片与 copy() 开辟了新的空间,产生的是新事物
# 例 2
lst2_1 = [111, 222, 333, 444]
lst2_2 = lst2_1
lst2_3 = lst2_1[:]
lst2_4 = lst2_1.copy() lst2_1[0] = 'a'
print("lst2_1 =", lst2_1)
print("lst2_2 =", lst2_2)
print("lst2_3 =", lst2_3)
print("lst2_4 =", lst2_4)
print('-'*30) lst2_2[1] = 'b'
print("lst2_1 =", lst2_1)
print("lst2_2 =", lst2_2)
print("lst2_3 =", lst2_3)
print("lst2_4 =", lst2_4)
print('-'*30) lst2_3[2] = 'c'
print("lst2_1 =", lst2_1)
print("lst2_2 =", lst2_2)
print("lst2_3 =", lst2_3)
print("lst2_4 =", lst2_4)
print('-'*30) lst2_4[3] = 'd'
print("lst2_1 =", lst2_1)
print("lst2_2 =", lst2_2)
print("lst2_3 =", lst2_3)
print("lst2_4 =", lst2_4)

>>>

lst2_1 = ['a', 222, 333, 444]
lst2_2 = ['a', 222, 333, 444]
lst2_3 = [111, 222, 333, 444]
lst2_4 = [111, 222, 333, 444]
------------------------------
lst2_1 = ['a', 'b', 333, 444]
lst2_2 = ['a', 'b', 333, 444]
lst2_3 = [111, 222, 333, 444]
lst2_4 = [111, 222, 333, 444]
------------------------------
lst2_1 = ['a', 'b', 333, 444]
lst2_2 = ['a', 'b', 333, 444]
lst2_3 = [111, 222, 'c', 444]
lst2_4 = [111, 222, 333, 444]
------------------------------
lst2_1 = ['a', 'b', 333, 444]
lst2_2 = ['a', 'b', 333, 444]
lst2_3 = [111, 222, 'c', 444]
lst2_4 = [111, 222, 333, 'd']

分析

  • 结合例 1,可以看出,对整个列表(一个完整对象)而言

    • 指向同一片地址的事物是“有难同当”的
    • 指向不同地址的事物是“井水不犯河水”的
# 例 3
lst3_1 = [0, 1, 2, [3, 4, 5]]
lst3_2 = lst3_1
lst3_3 = lst3_1[:]
lst3_4 = lst3_1.copy() print("id(lst3_1[0]) =", id(lst3_1[0]))
print("id(lst3_2[0]) =", id(lst3_2[0]))
print("id(lst3_3[0]) =", id(lst3_3[0]))
print("id(lst3_4[0]) =", id(lst3_4[0]))
print('-'*30) print("id(lst3_1[3]) =", id(lst3_1[3]))
print("id(lst3_2[3]) =", id(lst3_2[3]))
print("id(lst3_3[3]) =", id(lst3_3[3]))
print("id(lst3_4[3]) =", id(lst3_4[3]))
print('-'*30) print("id(lst3_1[3][0]) =", id(lst3_1[3][0]))
print("id(lst3_2[3][0]) =", id(lst3_2[3][0]))
print("id(lst3_3[3][0]) =", id(lst3_3[3][0]))
print("id(lst3_4[3][0]) =", id(lst3_4[3][0]))

>>>

id(lst3_1[0]) = 140733084593888
id(lst3_2[0]) = 140733084593888
id(lst3_3[0]) = 140733084593888
id(lst3_4[0]) = 140733084593888
------------------------------
id(lst3_1[3]) = 2523758754824
id(lst3_2[3]) = 2523758754824
id(lst3_3[3]) = 2523758754824
id(lst3_4[3]) = 2523758754824
------------------------------
id(lst3_1[3][0]) = 140733084593984
id(lst3_2[3][0]) = 140733084593984
id(lst3_3[3][0]) = 140733084593984
id(lst3_4[3][0]) = 140733084593984

分析

  • 这些列表内部,无论“第一层”还是“第二层”,对应元素的地址其实是相同的
# 例 4
lst4_1 = [0, 1, 2, [30, 31, 32]]
lst4_2 = lst4_1
lst4_3 = lst4_1[:]
lst4_4 = lst4_1.copy() lst4_1[3].append(33)
print("lst4_1 =", lst4_1)
print("lst4_2 =", lst4_2)
print("lst4_3 =", lst4_3)
print("lst4_4 =", lst4_4)
print('-'*40) lst4_3[3][0] = 66
print("lst4_1 =", lst4_1)
print("lst4_2 =", lst4_2)
print("lst4_3 =", lst4_3)
print("lst4_4 =", lst4_4)
print('-'*40) lst4_4[3][1] = 888
print("lst4_1 =", lst4_1)
print("lst4_2 =", lst4_2)
print("lst4_3 =", lst4_3)
print("lst4_4 =", lst4_4)

>>>

lst4_1 = [0, 1, 2, [30, 31, 32, 33]]
lst4_2 = [0, 1, 2, [30, 31, 32, 33]]
lst4_3 = [0, 1, 2, [30, 31, 32, 33]]
lst4_4 = [0, 1, 2, [30, 31, 32, 33]]
----------------------------------------
lst4_1 = [0, 1, 2, [66, 31, 32, 33]]
lst4_2 = [0, 1, 2, [66, 31, 32, 33]]
lst4_3 = [0, 1, 2, [66, 31, 32, 33]]
lst4_4 = [0, 1, 2, [66, 31, 32, 33]]
----------------------------------------
lst4_1 = [0, 1, 2, [66, 888, 32, 33]]
lst4_2 = [0, 1, 2, [66, 888, 32, 33]]
lst4_3 = [0, 1, 2, [66, 888, 32, 33]]
lst4_4 = [0, 1, 2, [66, 888, 32, 33]]

分析

  • 像例 4 这样列表中嵌套的列表(第二层),赋值、切片、copy() 都是“有难同当”的

2.3 copy 模块

# 例 5
import copy # 导入 copy 模块 lst5_1 = [0, 1, 2, [30, 31]]
lst5_2 = copy.copy(lst5_1) # 浅拷贝
lst5_3 = copy.deepcopy(lst5_1) # 深拷贝 print("lst5_1 =", lst5_1)
print("lst5_2 =", lst5_2)
print("lst5_3 =", lst5_3)
print('-'*40) print("id(lst5_1) =", id(lst5_1))
print("id(lst5_2) =", id(lst5_2))
print("id(lst5_3) =", id(lst5_3))
print('-'*40) lst5_1.append(4)
print("lst5_1 =", lst5_1)
print("lst5_2 =", lst5_2)
print("lst5_3 =", lst5_3)
print('-'*40) lst5_1[3].append(32)
print("lst5_1 =", lst5_1)
print("lst5_2 =", lst5_2)
print("lst5_3 =", lst5_3)

>>>

lst5_1 = [0, 1, 2, [30, 31]]
lst5_2 = [0, 1, 2, [30, 31]]
lst5_3 = [0, 1, 2, [30, 31]]
----------------------------------------
id(lst5_1) = 2523758966408
id(lst5_2) = 2523758966280
id(lst5_3) = 2523758881096
----------------------------------------
lst5_1 = [0, 1, 2, [30, 31], 4]
lst5_2 = [0, 1, 2, [30, 31]]
lst5_3 = [0, 1, 2, [30, 31]]
----------------------------------------
lst5_1 = [0, 1, 2, [30, 31, 32], 4]
lst5_2 = [0, 1, 2, [30, 31, 32]]
lst5_3 = [0, 1, 2, [30, 31]]

分析

  • 深、浅拷贝均与赋值不同
  • 切片、copy() 可以看作浅拷贝
  • 浅拷贝顶得住“第一层”却顶不住“第二层”
  • 深拷贝的“第二层”也“不容侵犯”
# 例 6
import copy # 导入 copy 模块 lst6_1 = {'a':"apple", 'b':["banana"], 'c':["carambola", "cherry", "coconut"]}
lst6_2 = copy.copy(lst6_1) # 浅拷贝
lst6_3 = copy.deepcopy(lst6_1) # 深拷贝 print("id(lst6_1) =", id(lst6_1))
print("id(lst6_2) =", id(lst6_2))
print("id(lst6_3) =", id(lst6_3))
print('-'*40) print("id(lst6_1['a']) =", id(lst6_1['a']))
print("id(lst6_2['a']) =", id(lst6_2['a']))
print("id(lst6_3['a']) =", id(lst6_3['a']))
print('-'*40) print("id(lst6_1['b']) =", id(lst6_1['b']))
print("id(lst6_2['b']) =", id(lst6_2['b']))
print("id(lst6_3['b']) =", id(lst6_3['b']))
print('-'*40) print("id(lst6_1['c']) =", id(lst6_1['c']))
print("id(lst6_2['c']) =", id(lst6_2['c']))
print("id(lst6_3['c']) =", id(lst6_3['c']))
print('-'*40) lst6_1['a'] = "arbutus"
print("lst6_1['a'] =", lst6_1['a'])
print("lst6_2['a'] =", lst6_2['a'])
print("lst6_3['a'] =", lst6_3['a'])
print('-'*40) lst6_1['b'].append("berry")
print("lst6_1['b'] =", lst6_1['b'])
print("lst6_2['b'] =", lst6_2['b'])
print("lst6_3['b'] =", lst6_3['b'])
print('-'*40) lst6_1['c'].remove("cherry")
print("lst6_1['c'] =", lst6_1['c'])
print("lst6_2['c'] =", lst6_2['c'])
print("lst6_3['c'] =", lst6_3['c'])

>>>

id(lst6_1) = 2298694416712
id(lst6_2) = 2298694594848
id(lst6_3) = 2298694594920
----------------------------------------
id(lst6_1['a']) = 2298693712952
id(lst6_2['a']) = 2298693712952
id(lst6_3['a']) = 2298693712952
----------------------------------------
id(lst6_1['b']) = 2298694533576
id(lst6_2['b']) = 2298694533576
id(lst6_3['b']) = 2298694533768
----------------------------------------
id(lst6_1['c']) = 2298694533704
id(lst6_2['c']) = 2298694533704
id(lst6_3['c']) = 2298694533640
----------------------------------------
lst6_1['a'] = arbutus
lst6_2['a'] = apple
lst6_3['a'] = apple
----------------------------------------
lst6_1['b'] = ['banana', 'berry']
lst6_2['b'] = ['banana', 'berry']
lst6_3['b'] = ['banana']
----------------------------------------
lst6_1['c'] = ['carambola', 'coconut']
lst6_2['c'] = ['carambola', 'coconut']
lst6_3['c'] = ['carambola', 'cherry', 'coconut']

分析

  • 字典与列表稍有不同
  • 若字典的值是列表,则该列表属于“第二层”

2.4 小结

  • 深拷贝,拷贝的是原对象内部的元素,是一个真正的副本

  • 浅拷贝,拷贝的是原对象内部数据的地址,并不是一个真正的副本

    • 拷贝后的新对象占用新的空间,但其内部的元素指向原对象内部对应元素的地址
    • “顶层拷贝”之称,即不变动拷贝后新对象的第一层元素
    • 当原对象中非第一层的可变元素发生变化时,新对象中的对应元素同步变化

2.5 copy 模块的补充

2.5.1 使用场合

  • copy 模块常用于复合对象

    • 复合对象:包含其他对象的对象,如列表、类实例等
  • 它不能拷贝模块、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组等
  • 必要时,可以重写 copy.copy(x) 和 copy.deepcopy(x[, memo])

2.5.2 深拷贝的问题与解决

问题

  1. 递归对象(直接或间接包含对自身引用的复合对象)可能导致递归循环
  2. 因为深拷贝会复制原对象的一切,所以可能复制过多的内容;例如,打算在副本之间共享的数据

解决方法

  1. 保存在当前复制过程中已经复制的对象的 memo 字典
  2. 让用户定义的类重写复制操作或复制的组件集

2.5.3 浅拷贝的作用

  1. 提前做浅拷贝可以防止后期因变量名众多而产生混乱
  2. 可应用于“联合账号”等

倘若阁下发现在下错误之处,还请不吝赐教!谢谢!

[Python3 填坑] 009 深拷贝与浅拷贝的更多相关文章

  1. [Python3 填坑] 006 “杠零”,空字符的使用

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 \0 是空字符,输出时看不到它,但它占 1 个字符的长度 2.2 \0 "遇八进制失效" 2.3 \0 与 '' 不 ...

  2. [Python3 填坑] 004 关于八进制

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 问题的由来 2.2 问题的解决 2.2.1 先说结论 2.2.2 八进制的用途 2.2.3 少废话,上例子 1. print( 坑的信息 ...

  3. [Python3 填坑] 001 格式化符号 & 格式化操作符的辅助指令

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 Python 格式化符号表 举例说明 (1) %c (2) %s 与 %d (3) %o (4) %x (5) %f (6) %e (7 ...

  4. [Python3 填坑] 012 字典的遍历在 Python2 与 Python3 中区别

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 Python2 中字典的遍历 2.2 Python3 中字典的遍历 2.3 结论 1. print( 坑的信息 ) 挖坑时间:2019/ ...

  5. [Python3 填坑] 005 如何“响铃”

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 问题的由来 2.2 问题的解决 1. print( 坑的信息 ) 挖坑时间:2019/01/08 明细 坑的编码 内容 Py004-2 ...

  6. [Python3 填坑] 003 关键字?保留字?预留字?

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 问题的由来 2.2 网上搜索 2.3 结论 2.4 后记 1. print( 坑的信息 ) 挖坑时间:2019/01/04 明细 坑的编 ...

  7. [Python3 填坑] 018 组装类的几个例子

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 MetaClass 举例 2.2 type 举例 2.3 MetaClass 举例 1. print( 坑的信息 ) 挖坑时间:2019 ...

  8. [Python3 填坑] 017 实例方法、静态方法、类方法的区别

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 先上例子 2.2 分析 1. print( 坑的信息 ) 挖坑时间:2019/04/07 明细 坑的编码 内容 Py024-1 实例方法 ...

  9. [Python3 填坑] 016 对 __getattr__ 和 __setattr__ 举例

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 __getattr__ 2.2 __setattr__ 1. print( 坑的信息 ) 挖坑时间:2019/04/07 明细 坑的编码 ...

随机推荐

  1. MixConv

    深度分离卷积一般使用的是3*3的卷积核,这篇论文在深度分离卷积时使用了多种卷积核,并验证了其有效性 1.大的卷积核能提高模型的准确性,但也不是越大越好.如下,k=9时,精度逐渐降低 2. mixCon ...

  2. Insomni'hack teaser 2019 - Pwn - 1118daysober

    参考链接 https://ctftime.org/task/7459 Linux内核访问用户空间文件:get_fs()/set_fs()的使用 漏洞的patch信息 https://maltekrau ...

  3. aidl 详解

    aidl 是 android interface define language 的缩写,主要是作为进程间通讯的一个接口规范,这种通讯是一种普通的 client-server 的模式,对于 clien ...

  4. 以太坊智能合约开发工具 Truffle 入门1

    Truffle是以太坊(Ethereum)智能合约开发的瑞士军刀,小巧好用,上手简单. 本篇文章主要展示如何用Truffle 开发第一个Ethereum智能合约. 1.准备工作:(本人针对window ...

  5. ssh跳板到其他服务器

    https://my.oschina.net/foreverich/blog/657075 http://mingxinglai.com/cn/2015/07/ssh-proxycommand/ 查看 ...

  6. gperftools尝试

    最近在找windows下比较好用的函数时间统计的库,听同事说gperftools是跨平台的,就下载下来尝试了一把.发现它确实实现了windows上可以调用的dll库文件(tcmalloc_minima ...

  7. 未能写入输出文件“c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\......”--“拒绝访问。 ”错误

    1.通常的解决方法:原因是由于系统目录下的Temp目录无相应的权限所致,具体操作如下: C:\Windows\temp-->属性-->安全-->编辑-->添加NETWORK S ...

  8. github-搜索功能

    in:name spring boot stars:>3000   //在标题上查找spring boot 并且 stars >3000 in:readme spring boot sta ...

  9. Day_03-函数和模块的使用

    使用函数求阶乘 使用while循环的代码: m = float(input('m = ')) n = float(input('n = ')) mn = m - n fm = 1 while m != ...

  10. 【PowerOJ1742&网络流24题】试题库问题(最大流)

    题意: 思路: [问题分析] 二分图多重匹配问题,用最大流解决. [建模方法] 建立二分图,每个类别为X集合中的顶点,每个题为Y集合中的顶点,增设附加源S和汇T. 1.从S向每个Xi连接一条容量为该类 ...