文档章节

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
程序员
私信 提问
Python动态类型的学习---引用的理解

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

herlang
2013/04/10
0
0
Python中的赋值、引用和深浅拷贝

全局变量 在函数之外创建的变量属于main,又被称为全局变量。它们可以在main中的任意函数中访问,与局部变量在函数结束时消失不同,全局变量可以在不同函数的调用之间持久存在。全局变量常常...

酒逢知己千杯少
2018/10/10
0
0
轻松使用OpenCV Python控制Webcam,读取Barcode

虽然手机上Barcode应用已经非常流行,但是工作的时候还是用Webcam比较方便。比如需要检测Barcode,我只需要拿Webcam对着电脑屏幕或者纸张扫一下就可以了。今天分享下如何轻松使用OpenCV控制W...

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

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

原创小博客
2018/08/29
0
0
jupyter、pyenv、virtualenv、virtualenvwrapper简要区别

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

xiaoge2016
2018/08/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【PG内核】pg11秒级新增非空默认字段的实现方法

pg11新特性,可以瞬间向一个表中添加非空默认字段。 今天研究了一下这个特性的内核实现方式,写个博客简单记录一下。 结论奉上 pg在从硬盘或者内存获取到一条数据记录后(以下称tuple),会使...

movead
10分钟前
0
0
如何保证MongoDB的安全性?

上周写了个简短的新闻《MongoDB裸奔,2亿国人求职简历泄漏!》: 根据安全站点HackenProof的报告,由于MongoDB数据库没有采取任何安全保护措施,导致共计202,730,434份国人求职简历泄漏。 然...

Fundebug
17分钟前
0
0
KVM

目录 (1):简介及安装 1. KVM 介绍 1.0 虚拟化简史 1.1 KVM 架构 2. KVM 的功能列表 3. KVM 工具集合 4. RedHat Linux KVM 安装 4.1 在安装 RedHat Linux 时安装 KVM 4.2 在已有的 RedHat...

临江仙卜算子
32分钟前
0
0
脚本配置java开发环境

@echo off&setlocal enabledelayedexpansion cls @echo "This script is used to registe envionment variables......" @echo. @echo. @echo. set var=%~dp0 set var=%var:~,-1% @echo "regi......

默克鱼
52分钟前
1
0
c++中友元函数理解与使用

在学习c++这一块,关于友元函数和友元类,感觉还是不好理解,但是井下心来,理解,需要把我一下几点。 首先讲友元函数。 (1)友元函数: 1)C++中引入友元函数,是为在该类中提供一个对外(除...

天王盖地虎626
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部