对象赋值实际上是简单的对象引用。也就是说当你创建一个对象,然后把它赋给另一个变量的时候,Python并没有拷贝这个对象,而只是拷贝了这个对象的引用。

假设想创建一对小夫妻的通用档案,名为person。然后你分别为他俩拷贝一份。

在下面的例子中,我们展示了两种拷贝对象的方式,一种使用了切片操作,另一种用了工厂方法,为了区分出三个不同的对象,我们使用id()内建函数来显示每个对象的标识符(还可以用is 操作符来做相同的事情)。第一步:

>>> person = ['name', ['savings',100.00]]

>>> hubby = person[:]                                   # slice copy

>>> wifey = list(person)       
                        # fac func copy

>>> [id(x) for x in person,hubby, wifey]

[23554728, ,
]          #三个不同的对象

现在为他们创建了初始有$100 的个人存款帐户。接下来将用户名改为定制的名字。并且让丈夫取走$50。第二步代码如下:

>>> hubby[0] = 'joe'

>>> wifey[0] = 'jane'

>>> hubby, wifey

(['joe', ['savings', 100.0]], ['jane',['savings', 100.0]])

>>> hubby[1][1] = 50.00

>>> hubby, wifey

(['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])

可见丈夫的行为影响到了妻子的账户,虽然我们进行了分开的拷贝作。为什么会这样呢?

原因是我们仅仅做了一个浅拷贝。对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原来对象元素的引用,换句话说,这个拷贝的对象本身是新的,但是它的内容不是。

在第一步进行完初始化之后,执行下列代码的结果如下:

>>> [id(x) for x in person]

[,
]

>>> [id(x) for x in hubby]

[,
]

>>> [id(x) for x in wifey]

[,
]

可见,三个列表虽然有各自的ID,但是列表中的元素的ID却是相同的。这也就是浅拷贝的意思。所以当执行hubby[1][1]
=50.00之后,它改变了三个列表中,共同指向的内部列表。因此这句代码影响到了三个列表。

序列类型对象的浅拷贝是默认类型拷贝,并可以以下几种方式实施:

(1)完全切片操作[:]

(2)利用工厂函数,比如list(),dict()等

(3)使用copy模块的copy函数.

你的下一个问题可能是:当妻子的名字被赋值,为什么丈夫的名字没有受到影响?难道它们的名字现在不应该都是'jane'了吗?为什么名字没有变成一样的呢?

这是因为在这两个列表的两个对象中,第一个对象是不可变的(是个字符串类型),而第二个是可变的(一个列表)。正因如此,当修改妻子的名字时,是新创建了一个字符串对象。

作完第二步之后,让我们分别看一下每个列表的元素的对象ID值。

>>> [id(x) for x in person]

[, 23575208]

>>> [id(x) for x in hubby]

[, 23575208]

>>> [id(x) for x in wifey]

[, 23575208]

可见,三个列表的第一个元素各不相同,但是第二个元素确实相同的引用。

如果需要的是两个分离账户,就需要进行深拷贝了.深拷贝----创建一个新的容器对象,包含原有对象元素(引用)全新拷贝的引用。这需要copy.deepcopy()函数.我们使用深拷贝来重写整个例子.

>>> person = ['name', ['savings',100.00]]

>>> hubby = person

>>> import copy

>>> wifey = copy.deepcopy(person)

>>> [id(x) for x in person,hubby, wifey]

[12242056, ,
]

>>> hubby[0] = 'joe'

>>> wifey[0] = 'jane'

>>> hubby, wifey

(['joe', ['savings', 100.0]], ['jane',['savings', 100.0]])

>>> hubby[1][1] = 50.00

>>> hubby, wifey

(['joe', ['savings', 50.0]], ['jane',['savings', 100.0]])

这就是我们想要的方式,作为验证,让我们确认一下所有四个对象都是不同的.

>>> [id(x) for x in hubby]

[12191712, 11826280]

>>> [id(x) for x in wifey]

[12114080, 12224792]

以下有几点关于拷贝操作的警告。

        第一:非容器类型(比如数字,字符串和其他"原子"类型的对象,像代码、类型和xrange对象等)没有拷贝一说。

第二:如果是元组类型,则切片拷贝和工厂函数拷贝,只是增加引用计数而已。如果使用copy.deepcopy函数,则会创建新的元组类型,但是还是浅拷贝。

>>> person = ('name', ['savings',100.00])

>>> hubby = person[:]

>>> wifey = tuple(person)

>>>

>>>

>>> [id(x) for x in person,hubby, wifey]

[23575888, 23575888,23575888]

>>> [id(x) for x in person]

[18859168, 23574848]

>>> [id(x) for x in hubby]

[18859168, 23574848]

>>> [id(x) for x in wifey]

[18859168, 23574848]

>>> wifey = copy.deepcopy(person)

>>> [id(x) for x in person,hubby, wifey]

[23575888, 23575888, ]

>>> [id(x) for x in wifey]

[18859168, 23554648]

>>> person = ['name', ('savings',100.00)]

>>> newPerson =copy.deepcopy(person)

>>> [id(x) for x in person,newPerson]

[12225352, 12226112]

>>> [id(x) for x in person]

[,
]

>>> [id(x) for x in newPerson]

[,
]

浅拷贝和深拷贝操作都可以在copy模块中找到。其实copy模块中只有两个函数可用:copy()进行浅拷贝操作,而deepcopy()进行深拷贝操作。

参考:《python核心编程》

Python深入:02浅拷贝深拷贝的更多相关文章

  1. python开发_copy(浅拷贝|深拷贝)_博主推荐

    在python中,有着深拷贝和浅拷贝,即copy模块 下面我们就来聊一下: 运行效果: ================================================== 代码部分: ...

  2. Python中的浅拷贝 深拷贝

    浅拷贝只拷贝父对象,子对象的地址空间不改变,包括下面三种: 1. copy 从下面的例子可以看出对象c从a拷贝,当对象a增加一个列表元素之后,c对象没有改变, 而当对象a中的子列表改变时,对象c的子列 ...

  3. PythonStudy1——Python 值拷贝 浅拷贝 深拷贝

    拷贝:对值进行复制的过程 # 值拷贝:应用场景最多  ls = [1, 'abc', [10]] ls1 = ls # ls1直接将ls中存放的地址拿过来  # ls内部的值发生任何变化,ls1都会随 ...

  4. python中赋值-浅拷贝-深拷贝之间的关系

    赋值: 变量的引用,没有拷贝空间 对象之间赋值本质上 是对象之间的引用传递而已.也就是多个对象指向同一个数据空间. 拷贝的对象分两种类型: . 拷贝可变类型 浅拷贝: 只拷贝第一层数据,不关心里面的第 ...

  5. Python练习四-浅拷贝&深拷贝

    一.数字.字符串不论是浅拷贝.深拷贝都是指向一个地址. a = 1 b = "abc" print (id(a)) print (id(b)) a1 = a b1 = b prin ...

  6. Python学习02 列表 List

    Python学习02 列表 List Python列表 List Python中的列表(List)用逗号分隔,方括号包围(comma-separated values (items) between ...

  7. Python网络02 Python服务器进化

    原文:Python网络02 Python服务器进化 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3. ...

  8. JS中有关对象的继承以及实例化、浅拷贝深拷贝的奥秘

    一.属性的归属问题 JS对象中定义的属性和方法如果不是挂在原型链上的方法和属性(直接通过如类似x的方式进行定义)都只是在该对象上,对原型链上的没有影响.对于所有实例共用的方法可直接定义在原型链上这样实 ...

  9. $.extend()浅拷贝深拷贝

    参考网址:http://bijian1013.iteye.com/blog/2255037 jQuery.extend() 函数用于将一个或多个对象的内容合并到目标对象. 注意:1. 如果只为$.ex ...

随机推荐

  1. springmvc框架使用拦截器实现301永久重定向,其实用过滤器应该是更好

    做seo的朋友提出要求 所有不带www.的访问需要301到带www的域名,以集中权重 可以使用过滤器检查servername带不带www,也可以使用拦截器 不会配置过滤器,所以先用拦截器实现吧,不过我 ...

  2. Java中用JXL导出Excel代码详解

    jxl是一个韩国人写的java操作excel的工具, 在开源世界中,有两套比较有影响的API可供使用,一个是POI,一个是jExcelAPI.其中功能相对POI比较弱一点.但jExcelAPI对中文支 ...

  3. 洛谷P1077 [NOIP2012普及组]摆花 [2017年四月计划 动态规划14]

    P1077 摆花 题目描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能 ...

  4. LintCode刷题笔记-- Distinct Subsequences

    标签:动态规划 题目描述: Given a string S and a string T, count the number of distinct subsequences of T in S. ...

  5. Apple Watch和Apple Pay将助苹果成为首家市值万亿美元公司

    苹果公司周二盘中市值首次突破7000亿美元,成为标普中首家市值超过7000亿美元的公司,市值是第二名Exxon的1.7倍.有分析师预测苹果在明年的市值将会逼近8000亿美元,不过如果参考著名激进投资人 ...

  6. UCloud-201809-001:Redis服务未授权访问漏洞安全预警

    UCloud-201809-001:Redis服务未授权访问漏洞安全预警 尊敬的UCloud用户,您好! 发布时间  2018-09-11更新时间  2018-09-11漏洞等级  HighCVE编号 ...

  7. Android4.0以上源码中的DiskLruCache类简介

    /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Versi ...

  8. 【OI】位运算操作

    一.基础操作 1.a<<b 将二进制a左移b位,不够的地方用0补位 例如 100<<2 == 10000 2.a>>b 将二进制a右移b位 例如 100>&g ...

  9. #define 和常量 const 的区别

    const 后的常量,程序对其中只能读不能修改. #include <iostream> using namespace std; int main() { const double pi ...

  10. firefox扩展开发(二):用XUL创建窗口控件

    firefox扩展开发(二):用XUL创建窗口控件 2008-06-11 16:57 1.创建一个简单的窗口 <?xml version="1.0"?> <?xm ...