zoukankan      html  css  js  c++  java
  • Python 关于拷贝(copy)汇总(列表拷贝 // 字典拷贝 // 自定义对象拷贝)

    1.列表拷贝

    引用是指保存的值为对象的地址。在 Python 语言中,一个变量保存的值除了基本类型保存的是值外,其它都是引用,因此对于它们的使用就需要小心一些。下面举个例子:

    问题描述:已知一个列表,求生成一个新的列表,列表元素是原列表的复制

    a=[1,2]
    b=a

    这种做法其实并未真正生成一个新的列表,b指向的仍然是a所指向的对象。这样,如果对a或b的元素进行修改,a,b的值同时发生变化。

    解决的方法为:

    a=[1,2]
    b=a[:]

    这样修改a对b没有影响。修改b对a没有影响。

    但 这种方法只适用于简单列表,也就是列表中的元素都是基本类型,如果列表元素还存在列表的话,这种方法就不适用了。原因就是,象a[:]这种处理,只是将列 表元素的值生成一个新的列表,如果列表元素也是一个列表,如:a=[1,[2]],那么这种复制对于元素[2]的处理只是复制[2]的引用,而并未生成 [2]的一个新的列表复制。为了证明这一点,测试步骤如下:

    >>> a=[1,[2]]
    >>> b=a[:]
    >>> b
    [1, [2]]
    >>> a[1].append(3)
    >>> a
    [1, [2, 3]]
    >>> b
    [1, [2, 3]]

    可见,对a的修改影响到了b。如果解决这一问题,可以使用copy模块中的deepcopy函数。修改测试如下:

    >>> import copy
    >>> a=[1,[2]]
    >>> b=copy.deepcopy(a)
    >>> b
    [1, [2]]
    >>> a[1].append(3)
    >>> a
    [1, [2, 3]]
    >>> b
    [1, [2]]

    2.字典拷贝

    python 中有两种方式可以实现将一个变量的值传递给另一个变量。
    一种是使用‘=’直接赋值。另一种是使用函数 copy();例如:

    >>> d = {'a':'a','b':'b'}
    >>> e = d.copy()
    >>> f = d
    >>> print (e,f)
    {'b': 'b', 'a': 'a'} {'b': 'b', 'a': 'a'}

    那么它们的效率又如何呢?我们再来看一下:

     1 import time
     2 d = {'a':'a','b':'b'}
     3 start = time.time()
     4 g = d.copy()
     5 end = time.time()
     6 print (end-start)
     7 ==>31.8900001049
     8 start = time.time()
     9 h = d
    10 end = time.time()
    11 print (end-start)
    12 ==>19.125

    我们发现使用‘=’直接赋值会比使用copy()函数快很多。为什么会这样呢?这是因为使用‘=’就等于是直接把这个d变量的引用地址赋给了h,也就是d和h两个变量都同时指向同一个存储地址,因此这种方式非常的快。而copy()呢,它不仅拷贝了d的值,而且拷贝了d的存储地址,也就是说d和g指向的是不同的存储地址。

    如下代码所示:

    >>> b['a']['b'] = 'm'
    >>> b
    {'b': 'b', 'a': {'b': 'm', 'a': 'a'}}
    >>> a
    {'b': 'b', 'a': {'b': 'm', 'a': 'a'}}
    >>> a = {'c':{'d':'d','e':'e'},'f':'f'}
    >>> b = a.copy()
    >>> a['f'] = 'qq'
    >>> print(a , b)
    {'c': {'d': 'd', 'e': 'e'}, 'f': 'qq'} {'c': {'d': 'd', 'e': 'e'}, 'f': 'f'}
    >>> a['c']['e'] = 'mm'
    >>> print(a , b)
    {'c': {'d': 'd', 'e': 'mm'}, 'f': 'qq'} {'c': {'d': 'd', 'e': 'mm'}, 'f': 'f'}

    字典的copy也是浅拷贝,如同list的拷贝原理相同,要实现深拷贝,使用deepcopy(),代码如下所示:

    >>> import copy
    >>> a = {'c':{'d':'d','e':'e'},'f':'f'}
    >>> b = copy.copy(a)
    >>> c = copy.deepcopy(a)
    >>> a['c']['e'] = 'mm'
    >>> print(a , b)
    {'c': {'d': 'd', 'e': 'mm'}, 'f': 'f'} {'c': {'d': 'd', 'e': 'mm'}, 'f': 'f'}
    >>> print(c)
    {'c': {'d': 'd', 'e': 'e'}, 'f': 'f'}

    3.字典拷贝遇到的问题示例:

    import copy
    
    def test(a_hash,b_hash):
        b_hash = copy.deepcopy(a_hash)
        print(b_hash)
    
    a = {}
    b = {}
    a[1] = 1
    
    test(a, b)
    print(b)
    ==>
    b_hash :  {1: 1}
    b :  {}

    在该程序中,为什么得到的b是空呢?

    在函数里面声明b是global的,而不是用似是而非的传值传指针,这样写是想说b_hash传递的是指针,可惜python里一旦用“=”赋值就已经相当于声明了一个新的变量,所以你的这个问题两种解决办法:

    方法一:在函数里声明你要修改的是全局变量
    def test(a_hash):
        global b
        b = copy.deepcopy(a_hash)
        print("b : ",b)
    
    a = {}
    b = {}
    a[1] = 1
    
    test(a)
    print("b : ",b)
    ==>
    b :  {1: 1}
    b :  {1: 1}
    方法二:把要传指针的东西放到列表里
    import copy
    
    def test(a_hash, b):
        b[0] = copy.deepcopy(a_hash)
        print("b[0] : ",b[0])
    
    a = {}
    b = [{}]
    a[1] = 1
    
    test(a , b)
    print("b : ",b)
    ==>
    b[0] :  {1: 1}
    b :  [{1: 1}]

    4.对象拷贝

    import copy
    class Foo:
        val = 1
        ch = "hhk"
        a = [1, [2, 3]]
    
    foo = Foo()
    
    cpy_foo = copy.copy(foo)
    print("foo : ",foo)
    print("cpy_foo : ",cpy_foo)
    ==>
    foo :  <__main__.Foo object at 0x010183D0>
    cpy_foo :  <__main__.Foo object at 0x0101EA10>
    
    cpy_foo.val = 1000
    print(cpy_foo.val)
    print(foo.val)
    ==>
    1000
    1
    
    cpy_foo.ch = "yes"
    print(cpy_foo.ch)
    print(foo.ch)
    ==>
    yes
    hhk
    
    cpy_foo.a[0] = 100
    print(cpy_foo.a)
    print(foo.a)
    ==>
    [100, [2, 3]]
    [100, [2, 3]]
    
    cpy_foo.a[1][0] = 100
    print(cpy_foo.a)
    print(foo.a)
    ==>
    [100, [100, 3]]
    [100, [100, 3]]
     
    print(id(foo.a), id(cpy_foo.a))

     ==>  12744224  12744224

    以上的代码中,对自定义对象进行拷贝,只能拷贝简单的类型,而对于list类型,不是拷贝,而是引用。

    由print(id(foo.a), id(cpy_foo.a))输出结果可知,foo对象和cpy_foo中的对象的lis在同一地址处。

     将上述代码中cpy_foo = copy.copy(foo) 改为 cpy_foo = copy.deepcopy(foo) 这两个对象中lis仍然在同一地址处,这说明自定义对象的list列表不能进行深拷贝。

    这是why???  有哪位大神清楚的请告知,谢谢~

     

  • 相关阅读:
    最优Django环境配置
    关于django migrations的使用
    js判断移动端是否安装某款app的多种方法
    Django升级1.9.6出现的中文本地化bug
    bootstrap响应式设计简单实践。
    Django单元测试简明实践
    Vue列表渲染
    vue 使用lib-flexable,px2rem 进行移动端适配 但是引入的第三方UI组件 vux 的样式缩小,解决方案
    Vue事件处理
    Vue表单输入绑定
  • 原文地址:https://www.cnblogs.com/fendou-999/p/3535118.html
Copyright © 2011-2022 走看看