2017-08-24 17 views
23

Khi chúng ta cần sao chép toàn bộ dữ liệu từ một từ điển chứa các kiểu dữ liệu nguyên thủy (để đơn giản, hãy bỏ qua sự hiện diện của kiểu dữ liệu như datetime vv), sự lựa chọn rõ ràng nhất mà chúng ta có là sử dụng deepcopy, nhưng deepcopy chậm hơn một số hackish khác phương pháp đạt được cùng một nghĩa là sử dụng serialization-unserialization ví dụ như json-dump-json-load hoặc msgpack-pack-msgpack-unpack. Sự khác biệt về hiệu quả có thể được nhìn thấy ở đây:Một cách nhanh chóng để sâu sắc chỉ là dữ liệu từ một dict python hoặc danh sách?

>>> 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 và msgpack (hoặc cPickle) phương pháp này là nhanh hơn so với một deepcopy bình thường, đó là hiển nhiên như deepcopy sẽ làm nhiều hơn nữa trong việc sao chép tất cả các thuộc tính của đối tượng quá .

Câu hỏi: Có cách nào sâu hơn/có sẵn để đạt được chỉ một bản sao dữ liệu của từ điển hoặc danh sách, mà không có tất cả chi phí mà bản in sâu có?

+7

Đó là hiếm khi hữu ích để đo lường hiệu suất trên một tập dữ liệu nhỏ và rút ra kết luận dựa trên đó. Nếu bạn có một cơ sở hạ tầng lồng nhau hoặc lớn hơn thì 'deepcopy' vẫn chậm hơn nhiều? – MSeifert

+0

@MSeifert Tôi đồng ý với phản hồi của bạn, nhưng ý định của tôi ở đây không phải là so sánh sâu với bất kỳ phương pháp nào, yêu cầu chính của tôi là cách giảm toàn bộ chi phí trên bản in sâu nếu tôi quan tâm chỉ là bản sao dữ liệu. – DhruvPathak

+1

có liên quan: https://stackoverflow.com/questions/24756712/deepcopy-is-extremely-slow và https://stackoverflow.com/questions/10128351/any-alternative-to-a-very-slow-deepcopy-in -a-dfs và https://stackoverflow.com/questions/8957400/what-is-the-runtime-complexity-of-pythons-deepcopy và https://writeonly.wordpress.com/2009/05/07/deepcopy -is-a-pig-cho-đơn giản-dữ liệu/ –

Trả lời

26

Nó thực sự phụ thuộc vào nhu cầu của bạn. deepcopy được xây dựng với ý định thực hiện (điều quan trọng nhất). Nó giữ tài liệu tham khảo được chia sẻ, nó không recurse vào cấu trúc đệ quy vô hạn và như vậy ... Nó có thể làm điều đó bằng cách giữ một từ điển memo trong đó tất cả gặp phải "vật" được chèn bằng cách tham chiếu. Đó là những gì làm cho nó khá chậm cho các bản sao dữ liệu thuần túy. Tuy nhiên, tôi sẽ gần như luôn luôn nói rằng deepcopycách đa chiều nhất để sao chép dữ liệu ngay cả khi các cách tiếp cận khác có thể nhanh hơn.

Nếu bạn có trong sạch dữ liệu và một số lượng hạn chế của các loại bên trong nó, bạn có thể xây dựng riêng của bạn deepcopy (xây dựng khoảng sau khi thực hiện 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) 

chỉ này hoạt động chính xác cho tất cả các bất biến các loại không chứa và listdict trường hợp. Bạn có thể thêm nhiều người điều phối nếu cần.

# 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) 

Chúng ta hãy cũng xem làm thế nào nó thực hiện khi sao chép một cuốn từ điển lớn chứa các chuỗi và số nguyên:

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) 
3

Tôi nghĩ bạn có thể thực hiện thủ công những gì bạn cần bằng cách ghi đè object.__deepcopy__.

Một cách nhiệt tình để làm điều này tôi nghĩ là sử dụng dict tùy chỉnh của bạn mở rộng từ dict sẵn có và thực hiện __deepcopy__ của bạn.

Các vấn đề liên quan