python 64式: 第43式、深拷贝源码分析

目标:
深拷贝源码分析

0 深浅拷贝基础
关键:
1 深拷贝:拷贝了对象所有元素,包含嵌套元素。是与原来对象无关的全新对象。无关性。
2 浅拷贝: 创建新对象,但新对象中各个元素是原对象对应各个元素的引用。

3 举例如下:
a = [1,[2,3]]
c = copy.copy(a)
那么a与c虽然是两个不同对象(id(a) != id(c)),但是a与c中各个元素是相同的(例如id(a[0]) = id(c[0]) , id[a[1]]=id(c[1]))
那么就是说:
例1: a.append(5)会导致a变成[1,[2,3],5],但由于a与c的地址不同,c并不会有变化。
例 2: a[1].append(6)会导致a变成[1,[2,3,6]],同样也会使得c变成[1,[2,3,6]],因为a[1] 与 c[1]地址相同,a[1]和c[1]实际是同一个对象。

总结:
深浅拷贝都是创建新的对象。但深拷贝包含了嵌套拷贝,拷贝后的对象与原对象无关。
浅拷贝中的每个元素和原对象每个元素相同,如果对元素操作,会使得浅拷贝对应元素发生变化。

参考:
https://www.cnblogs.com/xueli/p/4952063.html
https://www.cnblogs.com/huangbiquan/p/7795152.html
https://blog.csdn.net/w494675608/article/details/82114798


1 主入口
这里使用自己编写的一个demo作为总入口
import copy

def use_copy():
    a = [1, [2, 3]]
    b = copy.copy(a)
    c = copy.deepcopy(a)
    assert (id(a) != id(b))
    for i, value in enumerate(a):
        assert (id(a[i] == id(b[i])))
    a.append(5)
    assert b == [1, [2, 3]]
    assert c == [1, [2, 3]]
    a[1].append(6)
    assert b == [1, [2, 3, 6]]
    assert c == [1, [2, ]]
    
1.1 进入
E:\developSoftware\python27\Lib\copy.py
代码如下:
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier:
        y = copier(x, memo)
    else:
        try:
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
            issc = 0
        if issc:
            y = _deepcopy_atomic(x, memo)
        else:
            copier = getattr(x, "__deepcopy__", None)
            if copier:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor:
                        rv = reductor(2)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                y = _reconstruct(x, rv, 1, memo)

    memo[d] = y
    _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y
    
分析:
1.1) 变量分析
x的值: <type 'list'>: [1, [2, 3]]
memo的值: None
_nil的值: <type 'list'>: []

id(x)的值: 59984200
y的值: <type 'list'>: []
type(x)的值: <type 'list'>

1.2) 分析
copier = _deepcopy_dispatch.get(cls)
其中:
_deepcopy_dispatch是一个字典,建立了当前待拷贝对象的类型,到拷贝方法的键值对,内容如下:
<type 'dict'>: 
{<type 'instance'>: <function _deepcopy_inst at 0x0000000003663828>, 
<type 'dict'>: <function _deepcopy_dict at 0x00000000036636D8>, 
<type 'complex'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'instancemethod'>: <function _deepcopy_method at 0x0000000003663748>, 
<type 'builtin_function_or_method'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'ellipsis'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'weakref'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'function'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'NoneType'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'list'>: <function _deepcopy_list at 0x00000000036635F8>, 
<type 'tuple'>: <function _deepcopy_tuple at 0x0000000003663668>, 
<type 'type'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'bool'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'float'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'code'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'int'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'long'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'classobj'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'unicode'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'str'>: <function _deepcopy_atomic at 0x0000000003663588>, 
<type 'xrange'>: <function _deepcopy_atomic at 0x0000000003663588>}

1.3进入其中:
def _deepcopy_list(x, memo):
    y = []
    memo[id(x)] = y
    for a in x:
        y.append(deepcopy(a, memo))
    return y
d[list] = _deepcopy_list

分析:
1.3.1) 变量分析
x的值: <type 'list'>: [1, [2, 3]]
memo的值: <type 'dict'>: {}
然后变成:
<type 'dict'>: {59984200L: []}

1.3.2)
第一次循环:
a的值是1

然后递归进入:
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier:
        y = copier(x, memo)
    else:
        try:
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
            issc = 0
        if issc:
            y = _deepcopy_atomic(x, memo)
        else:
            copier = getattr(x, "__deepcopy__", None)
            if copier:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor:
                        rv = reductor(2)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                y = _reconstruct(x, rv, 1, memo)

    memo[d] = y
    _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y
    
分析
1)
x: 1
memo: <type 'dict'>: {59984200L: []}
nil: <type 'list'>: []
d: 47147720
y: <type 'list'>: []
cls: int
copier: <function _deepcopy_atomic at 0x0000000003663588>

def _deepcopy_atomic(x, memo):
    return x

y: 1
memo:
<type 'dict'>: {59984200L: [], 47147720L: 1}

2)
调用
    _keep_alive(x, memo) # Make sure x lives at least as long as d
具体参见1.2的分析


1.2) 分析
    _keep_alive(x, memo) # Make sure x lives at least as long as d

调用:
def _keep_alive(x, memo):
    """Keeps a reference to the object x in the memo.

    Because we remember objects by their id, we have
    to assure that possibly temporary objects are kept
    alive by referencing them.
    We store a reference at the id of the memo, which should
    normally not be used unless someone tries to deepcopy
    the memo itself...
    """
    try:
        memo[id(memo)].append(x)
    except KeyError:
        # aha, this is the first one :-)
        memo[id(memo)]=[x]

分析:
1.2.1)
x: 1
memo: <type 'dict'>: {59984200L: [], 47147720L: 1}
报错进入
    except KeyError:
        # aha, this is the first one :-)
        memo[id(memo)]=[x]
<type 'dict'>: {59984200L: [], 47147720L: 1}

进入:
<type 'dict'>: {59984200L: [], 47147720L: 1, 60816920L: [1]}
这里设置了本身的值。
返回:
y: 1


1.3) 递归返回
def _deepcopy_list(x, memo):
    y = []
    memo[id(x)] = y
    for a in x:
        y.append(deepcopy(a, memo))
    return y
    
分析:
1.3.1)
第一次循环后:
x: <type 'list'>: [1, [2, 3]]
y: <type 'list'>: [1]
memo: 
<type 'dict'>: {59984200L: [1], 47147720L: 1, 60816920L: [1]}

第二次循环:
a: <type 'list'>: [2, 3]

进入递归:
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier:
        y = copier(x, memo)
    else:
        try:
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
            issc = 0
        if issc:
            y = _deepcopy_atomic(x, memo)
        else:
            copier = getattr(x, "__deepcopy__", None)
            if copier:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor:
                        rv = reductor(2)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                y = _reconstruct(x, rv, 1, memo)

    memo[d] = y
    _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y
    
分析:
1.3.2)
x: <type 'list'>: [2, 3]
memo: 
<type 'dict'>: {59984200L: [1], 47147720L: 1, 60816920L: [1]}
id(x): 58755400
y: <type 'list'>: []
cls: <type 'list'>
copier:
<function _deepcopy_list at 0x00000000036635F8>

进入:
def _deepcopy_list(x, memo):
    y = []
    memo[id(x)] = y
    for a in x:
        y.append(deepcopy(a, memo))
    return y
    
分析:
x: <type 'list'>: [2, 3]
memo:
<type 'dict'>: {59984200L: [1], 47147720L: 1, 60816920L: [1]}
新的memo:
<type 'dict'>: {59984200L: [1], 47147720L: 1, 58755400L: [], 60816920L: [1]}

第一次循环:
a: 2
再次进入递归
id(x): 47147696
得到memo:
<type 'dict'>: 
{59984200L: [1], 47147720L: 1, 47147696L: 2, 
58755400L: [], 60816920L: [1]}

进入_keep_alive(x, memo)
新的memo:
<type 'dict'>: 
{59984200L: [1], 47147720L: 1, 47147696L: 2, 
58755400L: [], 60816920L: [1, 2]}

第二次循环:
a: 3
新的memo:
<type 'dict'>: 
{58755400L: [2, 3], 60816920L: [1, 2, 3], 
59984200L: [1], 
47147696L: 2, 47147720L: 1, 47147672L: 3}

返回y:
<type 'list'>: [2, 3]

1.4
此时返回到
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier:
        y = copier(x, memo)
    else:
        try:
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
            issc = 0
        if issc:
            y = _deepcopy_atomic(x, memo)
        else:
            copier = getattr(x, "__deepcopy__", None)
            if copier:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor:
                        rv = reductor(2)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                y = _reconstruct(x, rv, 1, memo)

    memo[d] = y
    _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y
    
分析:
刚才返回到
        y = copier(x, memo)
y的结果是:
<type 'list'>: [2, 3]
x: <type 'list'>: [2, 3]
id(x): 58755400
得到新的memo:
<type 'dict'>: 
{58755400L: [2, 3], 60816920L: [1, 2, 3], 
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}

1.4.1)进入:
_keep_alive(x, memo)

def _keep_alive(x, memo):
    """Keeps a reference to the object x in the memo.

    Because we remember objects by their id, we have
    to assure that possibly temporary objects are kept
    alive by referencing them.
    We store a reference at the id of the memo, which should
    normally not be used unless someone tries to deepcopy
    the memo itself...
    """
    try:
        memo[id(memo)].append(x)
    except KeyError:
        # aha, this is the first one :-)
        memo[id(memo)]=[x]
    
分析:
x: <type 'list'>: [2, 3]
memo:
<type 'dict'>: 
{58755400L: [2, 3], 
60816920L: [1, 2, 3], 
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}
得到新的memo:
<type 'dict'>: 
{58755400L: [2, 3], 
60816920L: [1, 2, 3, [2, 3]],
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}
分析:
可以发现:
60816920L: [1, 2, 3, [2, 3]],
也就是说memo本身的id是60816920L,最后memo本身的id这个键对应的值
就变成了新的拷贝之后的值

返回y:
<type 'list'>: [2, 3]

再回到:
def _deepcopy_list(x, memo):
    y = []
    memo[id(x)] = y
    for a in x:
        y.append(deepcopy(a, memo))
    return y
    
此时:
y: <type 'list'>: [1, [2, 3]]

此时又回到:
def deepcopy(x, memo)
    ......
    if copier:
        y = copier(x, memo)
    ......
    memo[d] = y
    _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y

分析:
x: <type 'list'>: [1, [2, 3]]
y: <type 'list'>: [1, [2, 3]]
id(x): 59984200
<type 'dict'>: 
{58755400L: [2, 3], 
60816920L: [1, 2, 3, [2, 3]], 
59984200L: [1, [2, 3]], 
47147696L: 2, 47147720L: 1, 47147672L: 3}

经过_keep_alive(x, memo)
def _keep_alive(x, memo):
    """Keeps a reference to the object x in the memo.

    Because we remember objects by their id, we have
    to assure that possibly temporary objects are kept
    alive by referencing them.
    We store a reference at the id of the memo, which should
    normally not be used unless someone tries to deepcopy
    the memo itself...
    """
    try:
        memo[id(memo)].append(x)
    except KeyError:
        # aha, this is the first one :-)
        memo[id(memo)]=[x]

memo:
<type 'dict'>: 
{58755400L: [2, 3], 
60816920L: [1, 2, 3, [2, 3], [1, [2, 3]]], 
59984200L: [1, [2, 3]], 
47147696L: 2, 47147720L: 1, 47147672L: 3}
可以看到:
60816920L的值从
60816920L: [1, 2, 3, [2, 3]], 
变成了
60816920L: [1, 2, 3, [2, 3], [1, [2, 3]]], 


2 总结
copy.deepcopy(x, memo=None)方法
def deepcopy(x, memo=None, _nil=[]):

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier:
        y = copier(x, memo)
    else:
        try:
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
            issc = 0
        if issc:
            y = _deepcopy_atomic(x, memo)
        else:
            copier = getattr(x, "__deepcopy__", None)
            if copier:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor:
                        rv = reductor(2)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                y = _reconstruct(x, rv, 1, memo)

    memo[d] = y
    _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y
主要的逻辑就是:
1)先判断待拷贝对象x的类型,根据不同类型调用对应的拷贝方法进行拷贝,
例如:对数组,则会遍历每个数组元素,再次递归调用deepcopy方法进行拷贝。
得到拷贝后的结果y后,设置字典memo[id(x)] = y
然后确保在memo中持有对对象x的引用。
2)其中memo是一个备忘录,保存已经拷贝的对象,防止循环拷贝。
所以memo是以待拷贝元素的id为key。

参考:
copy.deepcopy()源码
https://www.2cto.com/kf/201405/302337.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值