Chinaunix首页 | 论坛 | 博客
  • 博客访问: 411198
  • 博文数量: 26
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1101
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-04 22:28
个人简介

Never stop learning.

文章分类

全部博文(26)

文章存档

2014年(2)

2013年(24)

分类: Python/Ruby

2013-09-12 09:03:17

Python中的对象(四)

1、复制对象

  1. >>> LIST1 = [1, 2, 3]
  2. >>> LIST2 = LIST1[:]    # 列表的切片(slicing)功能
  3. >>> id(LIST1), id(LIST2)
  4. (4293721356L, 4293727372L)
  5. >>> LIST1[0] = 0
  6. >>> LIST1
  7. [0, 2, 3]
  8. >>> LIST2
  9. [1, 2, 3]
我们用列表的切片(slicing)功能生成一个和LIST1指向的对象一样的对象,其实就是复制了对象。很明显,LIST1和LIST2指向的
对象有不同的identity,即它们分别放在了不同内存地址里。所以LIST1和LIST2指向的是两个不同的的对象,只是此刻它们的值
是一样的。当我们改变其中一个的值时,另一个对象不会受到影响, 即不会随着改变 【1】 。过程如图1所示。

图 1. 隐式复制
从图中我们可以看出,当执行LIST1[0] = 0时,只是使LIST1的第一个元素指向0,LIST2的元素的绑定(引用)并没有变。我们之
所以称它为隐式复制,是因为列表切片(slicing)操作复制了一个同值的对象。
b. 显示复制
我们也可以用Python的标准库的copy模块来复制一个对象。它有两种方法,一种是浅拷贝(shallow copy),一种是深拷贝(deep
  1. import copy

  2. x = copy.copy(y) # make a shallow copy of y
  3. x = copy.deepcopy(y) # make a deep copy of y
copy)。这两种拷贝方法仅当被拷贝的对象是复合对象【2】时,它们会有不同。如果被拷贝对象是非复合对象,那么两种拷贝方法
是一样的,且不会真正的复制对象。
  1. >>> a = 1
  2. >>> b = copy.copy(a)
  3. >>> b
  4. 1
  5. >>> id(a), id(b)
  6. (31284744L, 31284744L)
    >>> c = copy.deepcopy(a)
  7. >>> c
    1
    >>> id(a), id(b), id(c)
    (31284744L, 31284744L, 31284744L)
a,b,c有相同的identity,都是31284744L,所以a,b, c指向同一个对象。
下面的例子解释了两种方法的不同。
b.1. 浅拷贝(shallow copy)
  1. >>> L1 = [[1,2], ['a', 'b']]
  2. >>> L2 = copy.copy(L1)
  3. >>> id(L1), id(L2)
  4. (39824136L, 39847240L)              # L1和L2分别指向两个不同的对象
  5. >>> L1
  6. [[1, 2], ['a', 'b']]
  7. >>> L2
  8. [[1, 2], ['a', 'b']]                # L2和L2的值是一样的,因为我们复制了对象。
  9. >>> L2[0][0] = 0                    # 改变L2指向的第一个对象的第一个元素的
  10. >>> L1
  11. [[0, 2], ['a', 'b']]
  12. >>> L2
  13. [[0, 2], ['a', 'b']]                # L1和L2指向的对象的值同时都改变了。
如果你试着改变L1指向的对象的元素值,效果是一样的。读者可以自行测试。shallow copy过程如图2所示:

图 2. Shallow Copy
从图中可以看出,shallow copy只是复制被拷贝对象的第一级(top level)对象。所以,对于第二级对象仍然是引用的关系。因此,
当我们改变第二级对象的值时,两个对象的值都会同时改变。
b2. 深拷贝(deep copy)
  1. >>> import copy
  2. >>> L1 = [[1, 2], ['a', 'b']]
  3. >>> L2 = copy.deepcopy(L1)
  4. >>> id(L1), id(L2)
  5. (40001608L, 40004488L)                # L1和L2分别指向两个不同的对象
  6. >>> L1
  7. [[1, 2], ['a', 'b']]
  8. >>> L2
  9. [[1, 2], ['a', 'b']]                  # L2和L2的值是一样的,因为我们复制了对象。
  10. >>> L1[0][0] = 0                      # 改变L1指向的第一个对象的第一个元素的
  11. >>> L1
  12. [[0, 2], ['a', 'b']]                 
  13. >>> L2
  14. [[1, 2], ['a', 'b']]                  # L2指向的对象的值并没有变化。
当L1指向的对象的第二级对象的元素变化时,对L2并没有影响。如果你试着改变L2指向的对象,也会获得同样的结果。Deep copy
的过程如图3所示:


图 3. Deep Copy
从图中可以看出,整个对象的结构被复制了,其实就是递归复制。
至此,Shallow Copy和Deep Copy的区别已经变得很清楚了。在实际的开发项目中,我们应该根据需要来选择对象复制的方法。

2、比较对象

在实际应用中,我们有时候需要比较两个对象。Python中比较两个对象涉及到两种方式: “==” 和 “is”。
“==”比较是测试两个被引用的对象的值是否相等。 看下面的代码块1,
代码块1
  1. >>> L1 = [1, 2, 3]
  2. >>> L2 = [1, 2, 3]
  3. >>> id(L1), id(L2)
  4. (40080968L, 40082824L)
  5. >>> L1 == L2
  6. True
    >>> L1 is L2
    False
  7. >>>
从上面的代码看,很显然L1和L2指向了不同的对象。但是这两个对象的值是相等。所以“==”比较的结果是True。
“is” 比较是测试两个对象是否是同一个对象。看下面的代码块2,
代码块2
  1. >>> L1 = [1, 2, 3]
  2. >>> L2 = L1
  3. >>> id(L1), id(L2)
  4. (40082824L, 40082824L)
  5. >>> L2 is L1
  6. True
  7. >>> L2 == L1
    True
  8. >>>
从上面的代码看,很显然L1和L2指向了同一个对象,因此L1和L2实际上就是同一个对象。所以“is”比较的结果是True。
实际上,“is”比较的是两个对象的identity(通过id()获取的一个整数值),如果identity相等则为True,否则则为False。
实际上在Python中,如果identity相等,必然是同一个对象。所以,在代码块2中“L1 is L2”的结果是True,在代码块1中
”L1 is L2“的结果是False。而 ”==“比较的仅仅是对象的值。同一个对象,值必然相等,不同对象,它们的值有可能相等,
有可能不相等。也就是说
如果”a is b"是True,那么“a == b”必定为True;
如果”a is b"是False,那么“a == b“可能为True或者False。
所以,我们可以把”==“看做是弱比较,把”is“看做是强比较。
由于Python不会重复创建非复合对象,一旦一个非复合对象被创建,它将会被引用数次,直到被垃圾回收掉。我们可以
用sys.getrefcount()来获得所给对象被引用的次数。
  1. >>> import sys
  2. >>> sys.getrefcount(True)
  3. 31
  4. >>> sys.getrefcount(0)
  5. 172
  6. >>>
至此,《Python中的对象》系列已全部分享完毕。Python这门语言最初确实是一门脚本语言。但是发展到现在,Python
已经远远超越了脚本语言的范畴。Python已经渗透到了软件开发的各个领域,并且越来越流行,越来越火了。学习Python,
使用Python,享受Python吧!
注:
【1】 前提是对象的值是非复合对象,如果是复合对象,其实也是Shallow Copy。
【2】 可以包含其他对象的对象叫复合对象。例如: 列表,字典,类实例等

by Harrison Feng in Python
阅读(5248) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~