深浅拷贝

ぐ巨炮叔叔 提交于 2020-08-13 18:45:09
"""Generic (shallow and deep) copying operations.

Interface summary:

        import copy

        x = copy.copy(y)        # make a shallow copy of y
        x = copy.deepcopy(y)    # make a deep copy of y

使用案例 第一个是浅拷贝,第二个是深拷贝。

For module specific errors, copy.Error is raised.

本模块使用到的特殊的异常,copy.Error

The difference between shallow and deep copying is only relevant for
compound objects (objects that contain other objects, like lists or
class instances).

深浅拷贝不同在于对容器类型的数据进行拷贝,容器类型有比如lists或者是class。

- A shallow copy constructs a new compound object and then (to the
  extent possible) inserts *the same objects* into it that the
  original contains.

浅拷贝将会构建一个新的容器对象,然后将里面的所有内容以引用的方式存一份,两个对象引用的是一样的。
也就是,这两个虽然说外形不同,但是本质相同,如果有容器类型,则内部数据处于共享状态。

- A deep copy constructs a new compound object and then, recursively,
  inserts *copies* into it of the objects found in the original.

深拷贝构建一个新的容器,然后原来所有成员的拷贝递归的形式进行重新构建,
并放入这个新的容器(类或list等),然后这两个对象从本质上不同,修改一个不会影响到另一个。

Two problems often exist with deep copy operations that don't exist
with shallow copy operations:

在深拷贝常常会遇到的问题。而浅拷贝不会遇到的问题。

 a) recursive objects (compound objects that, directly or indirectly,
    contain a reference to themselves) may cause a recursive loop

递归式的引用对象(一个容器,直接或者间接的引用到了自己),这样的对象拷贝会造成死递归。
比如声明一个类型为自身的成员变量。

 b) because deep copy copies *everything* it may copy too much, e.g.
    administrative data structures that should be shared even between
    copies

因为深拷贝将会拷贝所有的数据,有的时候数据链会太大。
如果想将一些数据容器进行共享,所以这也是一个问题。

Python's deep copy operation avoids these problems by:

python的解决方案。

 a) keeping a table of objects already copied during the current
    copying pass

在拷贝的时候维护一个表,表示已经拷贝的。

 b) letting user-defined classes override the copying operation or the
    set of components copied

给用户提供了一个可以自定义深拷贝行为的可重载函数。

This version does not copy types like module, class, function, method,
nor stack trace, stack frame, nor file, socket, window, nor array, nor
any similar types.

这个版本不支持拷贝类型:module, class, function, method,
nor stack trace, stack frame, nor file, socket, window, nor array, 获取其他类型.

Classes can use the same interfaces to control copying that they use
to control pickling: they can define methods called __getinitargs__(),
__getstate__() and __setstate__().  See the documentation for module
"pickle" for information on these methods.

可以用三个方法来控制拷贝,具体查看 pickle 模块。
"""

import types
import weakref
from copyreg import dispatch_table

class Error(Exception):
    pass
error = Error   # backward compatibility

try:
    from org.python.core import PyStringMap
except ImportError:
    PyStringMap = None

__all__ = ["Error", "copy", "deepcopy"]

def copy(x):
    """Shallow copy operation on arbitrary Python objects.

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

    cls = type(x)
    # 获取拷贝的类型
    copier = _copy_dispatch.get(cls)
    # 获取对应类型的拷贝方法
    if copier:
        return copier(x)
    #如果有对应的拷贝方法,则直接使用并返回。
    try:
        issc = issubclass(cls, type)
    except TypeError: # cls is not a class 提示,cls是一个type对象,不是一个class
        issc = False
    if issc:   #如果是type的一个子类,即是一个基本类型。
        # treat it as a regular class:  像普通的对象一样进行拷贝。
        return _copy_immutable(x)

    copier = getattr(cls, "__copy__", None)   #获取对象的__copy__成员方法,如果没有返回None
    if copier:
        return copier(x)    #如果对象实现了自己的copy版本,那么就使用对象的版本。

    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(shallow)copyable object of type %s" % cls)

    if isinstance(rv, str):  #如果是str类型就直接返回引用,因为str是不可变的。
        return x
    return _reconstruct(x, None, *rv)


_copy_dispatch = d = {}

def _copy_immutable(x):
    return x
for t in (type(None), int, float, bool, complex, str, tuple,
          bytes, frozenset, type, range, slice,
          types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented),
          types.FunctionType, weakref.ref):
    d[t] = _copy_immutable
#将这些类型都映射标记为不可变的。
t = getattr(types, "CodeType", None)
#因为文件也是一个对象,也会有编码格式。
if t is not None:
    d[t] = _copy_immutable

d[list] = list.copy
d[dict] = dict.copy
d[set] = set.copy
d[bytearray] = bytearray.copy

#这些是可变的类型,所以使用可变类型的函数方法。(可以通过函数名调用的方法,与静态方法也不同。)

if PyStringMap is not None:
    d[PyStringMap] = PyStringMap.copy

del d, t

#删除t d,保留了_copy_dispatch ,d只是为_copy_dispatch 打工的替身。

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,就设置为空dict
    d = id(x)    #获取传入对象的内存地址
    y = memo.get(d, _nil)    #在memo中找d对象,也就是是否有memo值传入。
    if y is not _nil:    #如果使用了内存地址做映射,则使用对应的映射值,这种是为了设置深拷贝的
                         #时候。设置特定值。
        return y

    cls = type(x)    #获取cls的type

    copier = _deepcopy_dispatch.get(cls)    #获取拷贝方法
    if copier:    #如果是可以进行拷贝的对象,也就是常见的基本类型,如int,float,list,tuple等
        y = copier(x, memo)     #调用对应类型的copy方法,如果是不可拷贝的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)    #如果有,则传入对应的memo,推荐使用
            else:    #如果没有定义deep拷贝
                reductor = dispatch_table.get(cls)     #则获取对应类型的,则用pickle策略
                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

_deepcopy_dispatch = d = {}

def _deepcopy_atomic(x, memo):
    return x
d[type(None)] = _deepcopy_atomic
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

def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
    y = [deepcopy(a, memo) for a in x]
    # We're not going to put the tuple in the memo, but it's still important we
    # check for it, in case the tuple contains recursive mutable structures.
    try:
        return memo[id(x)]
    except KeyError:
        pass
    for k, j in zip(x, y):
        if k is not j:
            y = tuple(y)
            break
    else:
        y = x
    return y
d[tuple] = _deepcopy_tuple

def _deepcopy_dict(x, memo, deepcopy=deepcopy):
    y = {}
    memo[id(x)] = y
    for key, value in x.items():
        y[deepcopy(key, memo)] = deepcopy(value, memo)
    return y
d[dict] = _deepcopy_dict
if PyStringMap is not None:
    d[PyStringMap] = _deepcopy_dict

def _deepcopy_method(x, memo): # Copy instance methods
    return type(x)(x.__func__, deepcopy(x.__self__, memo))
d[types.MethodType] = _deepcopy_method

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

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)
    if deep:
        memo[id(x)] = y

    if state is not None:
        if deep:
            state = deepcopy(state, memo)
        if hasattr(y, '__setstate__'):
            y.__setstate__(state)
        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

del types, weakref, PyStringMap

案例

import copy

class T:
    def __init__(self):
        self.a = [1]
        self.b = [2]
    def __deepcopy__(self, memodict={}):
        temp = T()
        temp.a = copy.deepcopy(self.a)
        temp.b = self.b
        return temp
    @property
    def show(self):
        print(self.a,self.b)

t = T()

t.a *=2
t.b *=2

t.show

temp = copy.deepcopy(t,{"a":t.a,"b":t.b})
# 在这里我们通过属性名的方式传入,可以选择将这些传入值进行深拷贝或者是浅拷贝,或者是
# 将这些值进行使用,比如说深拷贝计数等等作用。

t.a *=2
t.b *=2

t.show

temp.show
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!