代码之家  ›  专栏  ›  技术社区  ›  DhruvPathak

什么是一种快速的pythonic方法来深度复制python dict或list中的数据?

  •  25
  • DhruvPathak  · 技术社区  · 7 年前

    当我们需要从包含原始数据类型的字典中复制完整数据时(为了简单起见,让我们忽略datetime等数据类型的存在),最明显的选择是使用 deepcopy 但deepcopy要比其他一些黑客方法慢,例如使用序列化非序列化,例如json dump json load或msgpack pack msgpack unpack。从这里可以看出效率的差异:

    >>> import timeit
    >>> setup = '''
    ... import msgpack
    ... import json
    ... from copy import deepcopy
    ... data = {'name':'John Doe','ranks':{'sports':13,'edu':34,'arts':45},'grade':5}
    ... '''
    >>> print(timeit.timeit('deepcopy(data)', setup=setup))
    12.0860249996
    >>> print(timeit.timeit('json.loads(json.dumps(data))', setup=setup))
    9.07182312012
    >>> print(timeit.timeit('msgpack.unpackb(msgpack.packb(data))', setup=setup))
    1.42743492126
    

    json和msgpack(或cPickle)方法比普通的deepcopy快,这一点很明显,因为deepcopy在复制对象的所有属性方面也会做得更多。

    4 回复  |  直到 7 年前
        1
  •  0
  •   Suuuehgi    3 年前

    这取决于你的需要。 deepcopy memo 通过引用插入所有遇到的“事物”的字典。这使得纯数据拷贝的速度非常慢。无论如何,我会 几乎 总是这么说 深度复制 最具pythonic风格的数据复制方式 即使其他方法可以更快。

    如果你有纯数据和有限的类型,你可以建立自己的 深度复制 (构建 实施后 deepcopy in CPython ):

    _dispatcher = {}
    
    def _copy_list(l, dispatch):
        ret = l.copy()
        for idx, item in enumerate(ret):
            cp = dispatch.get(type(item))
            if cp is not None:
                ret[idx] = cp(item, dispatch)
        return ret
    
    def _copy_dict(d, dispatch):
        ret = d.copy()
        for key, value in ret.items():
            cp = dispatch.get(type(value))
            if cp is not None:
                ret[key] = cp(value, dispatch)
    
        return ret
    
    _dispatcher[list] = _copy_list
    _dispatcher[dict] = _copy_dict
    
    def deepcopy(sth):
        cp = _dispatcher.get(type(sth))
        if cp is None:
            return sth
        else:
            return cp(sth, _dispatcher)
    

    list dict

    # Timings done on Python 3.5.3 - Windows - on a really slow laptop :-/
    
    import copy
    import msgpack
    import json
    
    import string
    
    data = {'name':'John Doe','ranks':{'sports':13,'edu':34,'arts':45},'grade':5}
    
    %timeit deepcopy(data)
    # 11.9 µs ± 280 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    %timeit copy.deepcopy(data)
    # 64.3 µs ± 1.15 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    %timeit json.loads(json.dumps(data))
    # 65.9 µs ± 2.53 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    %timeit msgpack.unpackb(msgpack.packb(data))
    # 56.5 µs ± 2.53 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    data = {''.join([a,b,c]): 1 for a in string.ascii_letters for b in string.ascii_letters for c in string.ascii_letters}
    
    %timeit deepcopy(data)
    # 194 ms ± 5.37 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    %timeit copy.deepcopy(data)
    # 1.02 s ± 46.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    %timeit json.loads(json.dumps(data))
    # 398 ms ± 20.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    %timeit msgpack.unpackb(msgpack.packb(data))
    # 238 ms ± 8.81 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)