Chinaunix

标题: 一个关于深,浅拷贝问题。我这样理解对吗? [打印本页]

作者: lin_style    时间: 2009-03-30 17:02
标题: 一个关于深,浅拷贝问题。我这样理解对吗?
这个理解起来就是说,如果是一个容器(你可以理解为一个自定义的大型数据),而非系统数据类型,比如int float什么的,那么在拷贝的时候容器的大小是无法估量的。就像你在C/C++里面传递一个数组一样,你不可能把数整个拷贝过去,而仅仅传递一个指针.python中也一样,当容器间的拷贝时,只是产生类似指针一样的地址指向。
第一次实验:简单的=号赋值
>>> person=['name', ['savings', 100.00]]  //这里是一个容器
>>> hubby=person                                        //识别为类似指针
>>> wifey=person
>>> hubby[0]='abc'
>>> wifey[1][1]=789
>>> person,hubby,wifey
(['abc', ['savings', 789]], ['abc', ['savings', 789]], ['abc', ['savings', 789]])  /不相信可以用id()函数查看,内部地址一样

第二次实验:函数返回
>>> person=['name', ['savings', 100.00]]
>>> hubby = person[:]                                //一个函数返回,进行一个个元素拷贝。'name'是一个系统类型,被独立拷贝.而后者就是所谓的容器,产生                                                      //一个引用
>>> hubby[0]='abc'
>>> hubby[1][1]=789
>>> person,hubby
(['name', ['savings', 789]], ['abc', ['savings', 789]]) //第一个相当深拷贝了,所以不同。
作者: lin_style    时间: 2009-03-30 17:04
难道这两种赋值等号重载的方法不一样?
作者: lin_style    时间: 2009-03-30 17:09
忘记说环境了。
WINDOWS 2.5.3
PYTHON自带的编译器调试结果
作者: izhier    时间: 2009-03-30 17:18
讲错了!!!:wink:

[ 本帖最后由 izhier 于 2009-3-30 17:37 编辑 ]
作者: zhenglxd    时间: 2009-03-30 17:18
原帖由 lin_style 于 2009-3-30 17:09 发表
忘记说环境了。
WINDOWS 2.5.3
PYTHON自带的编译器调试结果

>>> import copy
>>> a=[1,2,[3,4]]
>>> b=a[:]
>>> c=copy.copy(a)
>>> d=copy.deepcopy(a)
>>> id(a)
20055504
>>> id(b)
20074624
>>> id(c)
20063456
>>> id(d)
20070528
>>> a[2][0]='a'
>>> print a
[1, 2, ['a', 4]]
>>> print b
[1, 2, ['a', 4]]
>>> print c
[1, 2, ['a', 4]]
>>> print d
[1, 2, [3, 4]]

作者: zhenglxd    时间: 2009-03-30 17:24
标题: 回复 #5 zhenglxd 的帖子
深和浅都生成新的对象
b和c是浅copy
d是deepcopy
对于列表中套列表
列表中的列表如果发生该表 浅copy会变 深copy不会
其他地方都一样
作者: zhenglxd    时间: 2009-03-30 17:25
标题: 回复 #6 zhenglxd 的帖子
深copy和浅copy都是copy生成一个新的对象
区别在于浅copy是直接copy 原始对象的ob[0]计数
比如说
a = [1, 2, 3, 4, ['a', 'b']]
在这里[1, 2, 3, 4, ['a', 'b']]是ob[0]
a是对他的引用ob[1]
所以任何对a的操作不会改变copy的结果但是对ob[0]的操作则会改变
而deep copy则是对象a的完全copy也就是不会随着a和父对象而改变


这个是我的笔记 要搞清楚是 改变引用还是改变本身。。
作者: teebye    时间: 2009-03-30 17:28
深拷贝是完全的克隆

浅拷贝复制对象的公共的属性
作者: izhier    时间: 2009-03-30 17:29
7楼说的对,上面我讲解有误,再改一下

  1. • A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to
  2. the objects found in the original.
  3. • A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found
  4. in the original.
复制代码

[ 本帖最后由 izhier 于 2009-3-30 17:32 编辑 ]
作者: lin_style    时间: 2009-03-30 17:48
==
等我吃饭回来看。
作者: lin_style    时间: 2009-03-30 18:59
不对

您还是没说清楚

hubby[0]='abc'
hubby = person[:]  
这两个等号赋值区别。

对于['name', ['savings', 100.00]]来说,
为什么前者是浅浅拷贝,后者是深浅拷贝?
作者: lin_style    时间: 2009-03-30 19:01
原帖由 zhenglxd 于 2009-3-30 17:18 发表

>>> import copy
>>> a=[1,2,[3,4]]
>>> b=a[:]
>>> c=copy.copy(a)
>>> d=copy.deepcopy(a)
>>> id(a)
20055504
>>> id(b)
20074624
>>> id(c)
20063456
>>> id(d)
20070528
>>> a[2][0]='a'
...



你输出这些ID没什么意义,这些只是表示变量本身的地址,不代表里面变量的值地址
至少也要
[id(x) for x in a]
作者: zhenglxd    时间: 2009-03-30 19:08
标题: 回复 #10 lin_style 的帖子
我以前也困惑过
我把我前面的回复总结下

copy.deepcopy是深copy
深copy 的意思就是所拷即所得,你copy对象是什么 那么新生成对象就是什么,他是个独立的对象,他的行为更像我们映像中的copy. 你不要想去其他的内部索引之类的,你所看到的是什么,deepcopy后就是什么,完全一样并且独立。

copy.copy()和 [:]都是浅copy
如果你的容器里 没有其他可变容器的情况下,那么他的行为和deepcopy一样,如果有其他可变容器比如说
>>> a=[1,2,3,{'a':'b'}]
>>> b=a[:]
>>> a[3]['a']='c'
>>> print a
[1, 2, 3, {'a': 'c'}]
>>> print b
[1, 2, 3, {'a': 'c'}]
上面的例子中 内部的容器跟着变了
在看下面的
>>> a[0]='a'
>>> print a
['a', 2, 3, {'a': 'c'}]
>>> print b
[1, 2, 3, {'a': 'c'}]
2者的区别是 一个是对 a进行修改,而b是个独立的copy 所以b没有变
但是 a[3]['a']='c' 修改的原始对象 所以修改被继承了


在看这个
>>> a=[1,2,3,[1,2,3]]
>>> b=a[:]
>>> a[3]=4
>>> print a
[1, 2, 3, 4]
>>> print b
[1, 2, 3, [1, 2, 3]]

他修改的是a[3] 所以b没跟着变
作者: zhenglxd    时间: 2009-03-30 19:17
原帖由 lin_style 于 2009-3-30 18:59 发表
不对

您还是没说清楚

hubby[0]='abc'
hubby = person[:]  
这两个等号赋值区别。

对于['name', ['savings', 100.00]]来说,
为什么前者是浅浅拷贝,后者是深浅拷贝?

没那么多复杂的东西

=是赋值符 ,仅仅是把某个对象赋值给一个变量,不是创建一个新的变量
a=b 和a=b[:]是2个概念

浅copy和深copy 都是创建了一个新的对象

a=b[:] 是浅copy, 只有copy.deepcopy才是深copy

再聚个例子
a=[1,2,3,[1,2,3]]
b=a[:]

上面的例子里 [1,2,3,[1,2,3]] 这个是对象本身 用ob[0]标记 表示没有引用
a=[1,2,3,[1,2,3]] 用ob[1]表示 a是对他的唯一引用
b=a[:] b是a的浅copy 如果a内部没有其他可变容器结构 那么 就等于深copy
所以如果 a[0],a[1]..之类基于a的成员被修改 不会改变b的成员

但是如果像a[3][0~n]被修改 则会改变b里相应的成员
应为 a[3][0~n] 修改的不是 a,他修改的是 ob[0]内部的

深copy则是 完全copy你看到什么就copy什么 不会发生变化
作者: lin_style    时间: 2009-03-30 21:00
原帖由 zhenglxd 于 2009-3-30 19:17 发表

没那么多复杂的东西

=是赋值符 ,仅仅是把某个对象赋值给一个变量,不是创建一个新的变量
a=b 和a=b[:]是2个概念

浅copy和深copy 都是创建了一个新的对象

a=b[:] 是浅copy, 只有copy.deepcopy才是深 ...



这句点化了我。谢谢。。大致明白了。不过我还是喜欢遵循传值和传指针的原则来看待。。虽然我知道应该从语言高处的抽象层来看,比如句柄就应该是个句柄的概念,套接字就是个套接字。
再次谢谢
作者: 碧蓝右耳    时间: 2009-04-08 16:24
我想 python源码分析 那本书在这个问题上 应该有很清楚的解释吧
吾翻书去也




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2