欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

从源代码层面分析 Python 的深度拷贝--深度拷贝--让你彻底理解深度拷贝的原理

最编程 2024-03-06 18:16:14
...

 

今天突然有兴趣研究一下python的深拷贝:

因为有循环调用 ,我在文中都是用1,2,3这样标明怎么跳代码的,可能要读者多翻一下,没有几页。

deepcopy()为入口函数:

Import copy  
if __name__ == '__main__':  
    # main(sys.argv)  
    list1 = [1,2,3]  
    # a = runManualTask()  
    b = copy.deepcopy(list1)   // 从这里入口

 从以下代码的 ->1 开始

def deepcopy(x, memo=None, _nil=[]):  // (->9, _deepcopy_list() 函数中会三次调用到这个函数->跳到10处
    """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) // (->1.这里就是获取被copy的是什么类型的,这里我们是用的list, 所以cls值为:list

    copier = _deepcopy_dispatch.get(cls) // (->2.这里就是用list 为key找到用哪个函数来copy,那如何找到的呢? 跳到3处继续看:
    if copier:   // (->6. 这里就是_deepcopy_list   
        y = copier(x, memo) // (->7. 调用_deepcopy_list()-> 跳到8看一下这个函数是怎么用的:
    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(4)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                if isinstance(rv, str):
                    y = x
                else:
                    y = _reconstruct(x, memo, *rv)

    # If is its own copy, don't memoize.
    if y is not x:
        memo[d] = y
        _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y  // (->10. deepcopy_list 每次调用这个函数的时候,会从这里返回list中单个元素的值->跳回到deepcopy_list函数中的11

 以下代码是->3

copier = _deepcopy_dispatch.get(cls)  // -> 3 从这里看这个_deepcopy_dispath是个什么东东

进到_deepcopy_dispatch 中发现

_deepcopy_dispatch = d = {}

第其实是指向d 的,那d里是什么呢?

从下边的代码可以看到,就是指什么样的类型用什么样的函数来copy:

def _deepcopy_atomic(x, memo):
    return x
d[type(None)] = _deepcopy_atomic // ->4 可以看到d[]中其实就是保存了所有的函数指针
d[type(Ellipsis)] = _deepcopy_atomic
d[type(NotImplemented)] = _deepcopy_atomic
d[int] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
d[complex] = _deepcopy_atomic
d[bytes] = _deepcopy_atomic
d[str] = _deepcopy_atomic
try:
    d[types.CodeType] = _deepcopy_atomic
except AttributeError:
    pass
d[type] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
d[weakref.ref] = _deepcopy_atomic

def _deepcopy_list(x, memo, deepcopy=deepcopy):
    y = []
    memo[id(x)] = y
    append = y.append
    for a in x:
        append(deepcopy(a, memo))
    return y
d[list] = _deepcopy_list  (  ->5. 那list应该是用这个函数指针来copy 数据的

 

这里知道是用_deepcopy_list    这个函数来copy了。那回到2的地方。我把上边的代码copy到下边来看:

下边的 ->2 之后就是 6,7 步骤了

def deepcopy(x, memo=None, _nil=[]):  // (->9, _deepcopy_list() 函数中会三次调用到这个函数->跳到10处
    """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) // (->1.这里就是获取被copy的是什么类型的,这里我们是用的list, 所以cls值为:list

    copier = _deepcopy_dispatch.get(cls) // (->2.这里就是用list 为key找到用哪个函数来copy,那如何找到的呢? 跳到3处继续看:
    if copier:   // (->6. 这里就是_deepcopy_list   
        y = copier(x, memo) // (->7. 调用_deepcopy_list()-> 跳到8看一下这个函数是怎么用的:
    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(4)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                if isinstance(rv, str):
                    y = x
                else:
                    y = _reconstruct(x, memo, *rv)

    # If is its own copy, don't memoize.
    if y is not x:
        memo[d] = y
        _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y  // ->10. deepcopy_list()函数里每次调用这个函数的时候,会从这里返回list中单个元素的值, 1 或2,或3; 当deepcopy_list() 已经完全执行完成后,这里返回的是 [1,2,3]

 

->8. 那我们来看一下  ->7 是怎么调用的   _deepcopy_list()

. def _deepcopy_list(x, memo, deepcopy=deepcopy):
    y = []
    memo[id(x)] = y
    append = y.append
    for a in x:  // 这里x 中list有3个元素[1,2,3],那就会分别调用3次deepcopy() 函数-> 跳到9处
        append(deepcopy(a, memo))  // 这里再次调用 deepcopy()时,转入的参数就是1,或者2, 或者3.然后每次返回的是一个整数。最终保存到y中。
    return y   // (->11,  3次调用deepcopy后,这里就已经复制好了。把Y返回回去。
d[list] = _deepcopy_list

 

上边的 _deepcopy_list()返回就是回到下图的Deepcopy()函数的7 处

Deepcopy()函数的7 处。然后跳过else{}->执行 10处的return y. 这样list 深copy完成。

def deepcopy(x, memo=None, _nil=[]):  // (->9, _deepcopy_list() 函数中会三次调用到这个函数->跳到10处
    """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) // (->1.这里就是获取被copy的是什么类型的,这里我们是用的list, 所以cls值为:list

    copier = _deepcopy_dispatch.get(cls) // (->2.这里就是用list 为key找到用哪个函数来copy,那如何找到的呢? 跳到3处继续看:
    if copier:   // (->6. 这里就是_deepcopy_list   
        y = copier(x, memo) // (->7. 调用_deepcopy_list()
    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(4)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                if isinstance(rv, str):
                    y = x
                else:
                    y = _reconstruct(x, memo, *rv)

    # If is its own copy, don't memoize.
    if y is not x:
        memo[d] = y
        _keep_alive(x, memo) # Make sure x lives at least as long as d
    return y  // 10 当调用最终完成进,这里返回的是一个新的数组,存的[1,2,3]

总结:

 深拷贝时,其实用到的嵌套调用。 在调用哪个函数时,用对象的类型来从dict中找用哪个函数,然后递归直到找到基本类型。最后返回新分配的空间给目标对象。