文档章节

python拷贝

acutesun
 acutesun
发布于 2017/07/23 10:36
字数 1373
阅读 3
收藏 0

先决条件

对于非容器类型(如数字、字符串、和其他’原子’类型的对象)没有拷贝这一说, 这种不可变对象存放在内存中的常量区. 该对象在常量区是唯一的
如果要使用该常量只有将内存地址赋值给对象的引用.

必要代码

import copy
listA = ['hello', 28, ['python', 'c#', 'javascript']]

打印列表A和B的id和打印A,B中元素的id

def print_id(listA, listB):
    print('listA:', id(listA))
    for ele in listA:
        print(str(ele) + ':', id(ele), end=' --> ')
    print()
    print('listB:', id(listB))
    for ele in listB:
        print(str(ele) + ':', id(ele), end=' --> ')
        

修改列表A的元素的值

def alter_A(listA): 
    listA[0] = 'hello222'
    listA[2].append('css')
    listA[1] = 55
    print(end='\n')
    print('分界线 对listA进行修改值操作:', end='\n \n')

将列表A赋值给列表B之后打印, 并将列表A元素的值修改后再次打印输出结果

def assignment():
    listB = listA
    print_id(listA, listB)
    alter_A(listA)
    print_id(listA, listB)

打印结果:

listA: 139643175769480
hello: 139643200213432 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 139643175768968 --> 
listB: 139643175769480
hello: 139643200213432 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 139643175768968 --> 
分界线 对listA进行修改值操作:
 
listA: 139643175769480
hello222: 139643175768368 --> 55: 10916096 --> ['python', 'c#', 'javascript', 'css']: 139643175768968 --> 
listB: 139643175769480
hello222: 139643175768368 --> 55: 10916096 --> ['python', 'c#', 'javascript', 'css']: 139643175768968 --> 

从打印结果可以看出所有的id都是相同的,对列表A进行修改就是对B修改

  • 结论: Python中,对象的赋值都是进行对象引用(内存地址)传递, listA is listB

将列表A浅拷贝给列表B之后打印, 并将列表A元素的值修改后再次打印输出结果

def light_copy():
    listB = copy.copy(listA)
    print_id(listA, listB)
    alter_A(listA)
    print_id(listA, listB)
    

打印结果:


listA: 140716204897416
hello: 140716229341624 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 140716204897160 --> 
listB: 140716204908872
hello: 140716229341624 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 140716204897160 --> 
分界线 对listA进行修改值操作:
 
listA: 140716204897416
hello222: 140716204896560 --> 55: 10916096 --> ['python', 'c#', 'javascript', 'css']: 140716204897160 --> 
listB: 140716204908872
hello: 140716229341624 --> 28: 10915232 --> ['python', 'c#', 'javascript', 'css']: 140716204897160 --> 

1.从输出可以看出浅拷贝创建了一个新的对象, 所以listA is not listB

2.但是对象中的元素, 浅拷贝会使用原来元素的引用(内存地址), 也就是说listA[n] is listB[n]

3.在对listA中的元素修改后, listA[0],listA[1]是不可变对象,所以指向了新的地址空间. listB[1],listB[0]仍然指向原来的地址空间.而listA[2]是一个可变对象列表.对其进行修改并不会指向一个新的地址空间. listB[2]也是指向相同的地址空间,所以listB[2]的值也被改变

  • 结论: Python中,浅拷贝会创建新对象, 但是仍然会使用原来对象指向的地址空间 浅拷贝之所以称为浅拷贝,是它仅仅只拷贝了一层,如果里面有嵌套对象,则不会创建新的对象,仍然使用原来的引用

  • 补充: 以下操作会产生浅拷贝

    使用切片[:]操作
    使用工厂函数(如list/dir/set)
    使用copy模块中的copy()函数

将列表A深拷贝给列表B之后打印, 并将列表A元素的值修改后再次打印输出结果

def deep_copy():
    listB = copy.deepcopy(listA)
    print_id(listA, listB)
    alter_A(listA)
    print_id(listA, listB)

打印结果:

listA: 140461809181960
hello: 140461833626040 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 140461809182216 --> 
listB: 140461809181896
hello: 140461833626040 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 140461809181768 --> 
分界线 对listA进行修改值操作:
 
listA: 140461809181960
hello222: 140461809180976 --> 55: 10916096 --> ['python', 'c#', 'javascript', 'css']: 140461809182216 --> 
listB: 140461809181896
hello: 140461833626040 --> 28: 10915232 --> ['python', 'c#', 'javascript']: 140461809181768 --> 

  1. 深拷贝同样会创建一个全新的对象 listA: 140461809181960 listB: 140461809181896 . 所以 listA is not listB

  2. 对象中的元素,深拷贝都会重新生成一份(有特殊情况, 比如listA中的字符串, 整数等, 开头已经介绍过), 而不是向浅拷贝那样使用原始元素的引用(内存地址)

  3. 深拷贝后 listA[2]的内存地址是140461809182216, listB[2]140461809181768
    所以 listA[2] is not listB[2].

  4. listA和listB中的字符串和整数对象都是指向的同一个内存地址, 对listA进行修改实质是指向了 新的内存地址. 而listB不变. 所以当对listA元素修改完全不影响listB, 两者是完全不同的对象

    注意: 每次打印结果可能不同

  • 结论:深拷贝会生成完全不同的对象

不可变对象不能进行深拷贝

def deep_copy2():
    a = ('abc', 12, (1, ))
    b = copy.deepcopy(a)
    print(a is b)

True


参考: link

总结

  • Python中对象的赋值都是进行对象引用(内存地址)传递

  • 使用copy.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素,依然使用原始的引用.

  • 如果需要复制一个容器对象,以及它里面的所有元素(包含元素的子元素),可以使用copy.deepcopy()进行深拷贝

  • 对于非容器类型(如数字、字符串、和其他’原子’类型的对象)没有被拷贝一说

  • 如果元祖变量只包含原子类型(不可变)对象,则不能深拷贝


完整代码:

import copy
listA = ['hello', 28, ['python', 'c#', 'javascript']]
def print_id(listA, listB):
    print('listA:', id(listA))
    for ele in listA:
        print(str(ele) + ':', id(ele), end=' --> ')
    print()
    print('listB:', id(listB))
    for ele in listB:
        print(str(ele) + ':', id(ele), end=' --> ')
def alter_A(listA):
    listA[0] = 'hello222'
    listA[2].append('css')
    listA[1] = 55
    print(end='\n')
    print('分界线 对listA进行修改值操作:', end='\n \n')
def assignment():
    listB = listA
    print_id(listA, listB)
    alter_A(listA)
    print_id(listA, listB)
def light_copy():
    listB = copy.copy(listA)
    print_id(listA, listB)
    alter_A(listA)
    print_id(listA, listB)
def deep_copy():
    listB = copy.deepcopy(listA)
    print_id(listA, listB)
    alter_A(listA)
    print_id(listA, listB)
def deep_copy2():
    a = ('abc', 12, (1, ))
    b = copy.deepcopy(a)
    print(a is b)
if __name__ == '__main__':
    # assignment()
    # light_copy()
    deep_copy2()

© 著作权归作者所有

共有 人打赏支持
acutesun
粉丝 0
博文 71
码字总数 83152
作品 0
程序员
私信 提问
最常见的 35 个 Python 面试题及答案(2018 版)

雷锋网按:本文为 AI 研习社编译的技术博客,原文 Top 35 Python Interview Questions and Answers in 2018 ,作者 DataFlair Team。 翻译 | 于志鹏 整理 | 凡江 1. Python 面试问题及答案 ...

雷锋字幕组
08/02
0
0
jupyter、pyenv、virtualenv、virtualenvwrapper简要区别

一、区别 1.jupyter 对接ipython,作为一个web端的notebook,便于python工作。 2.pyenv 在创建一个新的python版本时,完全拷贝一个现成的python环境。新的python版本,可作为global 3.virtua...

xiaoge2016
08/10
0
0
python编程之赋值和拷贝的区别概述及操作excel数据库(图)

python编程之赋值和拷贝的区别概述及操作excel数据库(图) 一、赋值 在Python中,对象的赋值就是简单的对象引用,这点和C++不同,如下所示: a = [1,2,”hello”,[‘python’, ‘C++’]] b ...

原创小博客
08/29
0
0
Python动态类型的学习---引用的理解

一,Python浅拷贝和深拷贝 浅拷贝是指拷贝的只是原对象元素的引用,换句话说,浅拷贝产生的对象本身是新的,但是它的内容不是新的,只是对原对象的一个引用。这里有个例子 >>> aList=[[1, 2...

herlang
2013/04/10
0
0
【转载】如何制作python安装模块(setup.py)

Python 模块的安装方法: 单文件模块:直接把文件拷贝到 $PYTHON_DIR/lib 多文件模块,且带有 setup.py :只需执行 python setup.py install egg 文件:1) 下载 ezsetup.py ,运行 python e...

摩云飞
2013/01/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS 安装PHP5和PHP7

安装PHP5 下载解压二进制包 [root@test-a src]# cd /usr/local/src/[root@test-a src]# wget http://cn2.php.net/distributions/php-5.6.32.tar.bz2[root@test-a src]# tar jxvf php-5.6......

野雪球
今天
4
0
windows上类似dnsmasq的软件Dual DHCP DNS Server

官网地址:http://dhcp-dns-server.sourceforge.net/官网定向的下载地址:https://sourceforge.net/projects/dhcp-dns-server/files/ 设置参考地址:http://blog.51cto.com/zhukeqiang/18264......

xueyuse0012
今天
3
0
LinkedHashMap源码解析

前言 HashMap中的元素时无序的,也就是说遍历HashMap的时候,顺序和放入的顺序是不一样的。 如果需要有序的Map,就可以采用LinkedHashMap. LinkedHashMap通过维护一个包含所有元素的双向链表,...

grace_233
今天
3
0
初识flask

文档 0.10.1版本 http://www.pythondoc.com/flask/index.html 1.0.2版本 https://dormousehole.readthedocs.io/en/latest/ 安装flask $ pip3 install flaskCollecting flask Downloading......

yimingkeji
昨天
6
0
Akka系统《sixteen》译

Actor是一个封装状态(state)和行为(behavior)的对象,它们只通过交换消息通信(放入收件人邮箱的邮件)。从某种意义上说,Actor是最严格的面向对象编程形式,但它更适合将他们视为人:在与Act...

woshixin
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部