Source code reading | The principle of copy.deepcopy in Python

Introduction

Deepcopy is used for deep copying, but in practice, you may encounter that after copying an object, some attributes are gone, so look at the source code and record it by the way.
Python version: 3.6
copy module location: copy.py (Python The
principle of deepcopy is summarized: the logic of deepcopy needs to change according to the different objects, such as float, int, etc., just copy one directly, if it is a complex object, all attributes must be copied. deepcopy object Potential problems are, one is that there may be a circular reference (for example, a certain property of the object refers to itself), and the other is how to construct a new object as a copy of the object. The
first problem is solved by a memo parameter, if a certain property is found The object corresponding to an id is copied, and it is no longer copied. The
second solution is to obtain the parameter representation of the object through the __reduce__sum __reduce_ex__function of the object, and then copy the parameter representation, and then _reconstruceconstruct an object through the function to get the object A deepcopy of __deepcopy__an object . It is also possible to customize the copy logic of an object through functions.

Source code comments

"""
deepcopy的主要逻辑在下面的函数.
deepcopy的文档: https://docs.python.org/3/library/copy.html
"""

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 = {
    
    }
	# memo 用于记录对象是否被复制过了, 可以防止对象出现循环引用导致无限复制的情景.
    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)  # 一些内置的数据类型有特定的复制函数, 比如list等, 在copy.py中可以找到其定义.
    if copier:
        y = copier(x, memo)
    else:
        try:
            issc = issubclass(cls, type)  # int, float等类型, 在复制的时候直接是自身, 因此不用特别的进行复制.
        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)  # 对象可以自己定义deepcopy的逻辑
            else:
            	# 如果没有定义, 则通过reduce进行对象的深层复制. reduce和_reconstruct是对应的, reduce是对象的序列化, _reconstruct是根据对象的序列化表示构建对象. 这里是deepcopy的关键, 关于序列化和反序列化的操作, 和pickle的逻辑是一样的. 参考 pickle的文档: https://docs.python.org/3/library/pickle.html
                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

"""
_reconstruct是根据reduce得到的对象表示进行构建对象. 在deepcopy的情况下, 是复制对象序列化的表示, 然后根据这个表示构建对象的复制, 整个过程是迭代进行的.
"""
def _reconstruct(x, memo, func, args,
                 state=None, listiter=None, dictiter=None,
                 deepcopy=deepcopy):
    deep = memo is not None
    if deep and args:
        args = (deepcopy(arg, memo) for arg in args)
    y = func(*args)  # 对象的构建, 除了x, 和memo, 其他几个参数都是reduce得到的, 这里调用函数func默认是内置的, 不是对象的构造函数(__init__). 
    if deep:
        memo[id(x)] = y

    if state is not None:
        if deep:
            state = deepcopy(state, memo)
        if hasattr(y, '__setstate__'):
            y.__setstate__(state)  # setstate的语义见pickle的文档.
        else:
            if isinstance(state, tuple) and len(state) == 2:
                state, slotstate = state
            else:
                slotstate = None
            if state is not None:
                y.__dict__.update(state)
            if slotstate is not None:
                for key, value in slotstate.items():
                    setattr(y, key, value)

    if listiter is not None:
        if deep:
            for item in listiter:
                item = deepcopy(item, memo)
                y.append(item)
        else:
            for item in listiter:
                y.append(item)
    if dictiter is not None:
        if deep:
            for key, value in dictiter:
                key = deepcopy(key, memo)
                value = deepcopy(value, memo)
                y[key] = value
        else:
            for key, value in dictiter:
                y[key] = value
    return y

Guess you like

Origin blog.csdn.net/feifei3211/article/details/111937002