zoukankan      html  css  js  c++  java
  • Python深入:02浅拷贝深拷贝

            对象赋值实际上是简单的对象引用。也就是说当你创建一个对象,然后把它赋给另一个变量的时候,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, 23576168, 23574848        #三个不同的对象

     

            现在为他们创建了初始有$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]

    [18859168, 23575208]

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

    [18859168, 23575208]

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

    [18859168, 23575208]

     

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

     

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

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

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

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

     

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

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

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

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

    [18859168, 23575208]

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

    [23601928, 23575208]

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

    [23571296, 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, 12242056, 12224232]

    >>> 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, 23575808]

    >>> [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]

    [9919616, 11800088]

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

    [9919616, 11800088]

     

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

     

    参考:《python核心编程》

  • 相关阅读:
    iOS 动画 旋转 移动简单代码
    iOS 根据UIImage 修改UIImageView Frame (包括截取图片中间部分)
    Spark深入浅出
    Spark企业级开发最佳实践
    Spark架构案例鉴赏:Conviva、Yahoo!、优酷土豆、网易、腾讯、淘宝等公司的实际Spark案例
    Spark 1.0.0企业级开发动手:实战世界上第一个Spark 1.0.0课程,涵盖Spark 1.0.0所有的企业级开发技术
    Hadoop大数据时代:Hadoop&YarnSpark企业级最佳实践 (4天)
    Hadoop&Yarn&Mahout&Spark企业级最佳实践(3天)
    Conquer Big Data through Spark
    18小时内掌握Spark:把云计算大数据速度提高100倍以上!(Final版本)
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247186.html
Copyright © 2011-2022 走看看