python拷贝
博客专区 > acutesun 的博客 > 博客详情
python拷贝
acutesun 发表于5个月前
python拷贝
  • 发表于 5个月前
  • 阅读 2
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

先决条件

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

必要代码

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()

共有 人打赏支持
粉丝 0
博文 65
码字总数 83152
×
acutesun
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: