Câu hỏi Làm thế nào để hợp nhất hai từ điển trong một biểu thức?


Tôi có hai từ điển Python, và tôi muốn viết một biểu thức duy nhất trả về hai từ điển này, được hợp nhất. Các update() phương pháp sẽ là những gì tôi cần, nếu nó trả về kết quả của nó thay vì sửa đổi một dict tại chỗ.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

Làm thế nào tôi có thể nhận được dict sáp nhập cuối cùng trong z, không phải x?

(Để rõ ràng hơn, xử lý xung đột một lần cuối cùng của dict.update() là những gì tôi đang tìm kiếm là tốt.)


3222
2017-09-02 07:44


gốc




Các câu trả lời:


Làm thế nào tôi có thể hợp nhất hai từ điển Python trong một biểu thức?

Đối với từ điển x và y, z trở thành từ điển được hợp nhất với các giá trị từ y thay thế những người từ x.

  • Trong Python 3.5 trở lên,:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • Trong Python 2, (hoặc 3.4 hoặc thấp hơn) viết một hàm:

    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z
    

    z = merge_two_dicts(x, y)
    

Giải trình

Giả sử bạn có hai dicts và bạn muốn hợp nhất chúng thành một dict mới mà không thay đổi các dicts gốc:

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

Kết quả mong muốn là lấy từ điển mới (z) với các giá trị được sáp nhập và giá trị của dict thứ hai ghi đè những giá trị đó từ giá trị đầu tiên.

>>> z
{'a': 1, 'b': 3, 'c': 4}

Một cú pháp mới cho điều này, được đề xuất trong PEP 448 và có sẵn dưới dạng Python 3.5, Là

z = {**x, **y}

Và nó thực sự là một biểu hiện duy nhất. Nó hiện đang hiển thị như được thực hiện trong lịch phát hành cho 3.5, PEP 478và giờ đây nó đã đi vào Có gì mới trong Python 3.5 tài liệu.

Tuy nhiên, vì nhiều tổ chức vẫn đang sử dụng Python 2, bạn có thể thực hiện điều này theo cách tương thích ngược. Theo cách cổ điển Pythonic, có sẵn trong Python 2 và Python 3.0-3.4, là làm điều này như là một quá trình gồm hai bước:

z = x.copy()
z.update(y) # which returns None since it mutates z

Trong cả hai cách tiếp cận, y sẽ đến thứ hai và các giá trị của nó sẽ thay thế xgiá trị của, do đó 'b' sẽ trỏ đến 3 trong kết quả cuối cùng của chúng tôi.

Chưa có trên Python 3.5, nhưng muốn biểu thức đơn

Nếu bạn chưa ở trên Python 3.5, hoặc cần phải viết mã tương thích ngược, và bạn muốn điều này trong một biểu thức đơn, hiệu suất tốt nhất trong khi cách tiếp cận chính xác là đặt nó vào một hàm:

def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

và sau đó bạn có một biểu thức duy nhất:

z = merge_two_dicts(x, y)

Bạn cũng có thể tạo một hàm để hợp nhất một số dicts không xác định, từ 0 đến một số rất lớn:

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

Hàm này sẽ làm việc trong Python 2 và 3 cho tất cả các dicts. ví dụ. cho dicts a đến g:

z = merge_dicts(a, b, c, d, e, f, g) 

và các cặp giá trị khóa trong g sẽ được ưu tiên hơn người dicts a đến f, v.v.

Critiques of Other Các Câu Trả Lời

Không sử dụng những gì bạn thấy trong câu trả lời được chấp nhận trước đây:

z = dict(x.items() + y.items())

Trong Python 2, bạn tạo hai danh sách trong bộ nhớ cho mỗi dict, tạo một danh sách thứ ba trong bộ nhớ với chiều dài bằng chiều dài của hai đầu tiên đặt lại với nhau, và sau đó loại bỏ tất cả ba danh sách để tạo ra dict. Trong Python 3, điều này sẽ thất bại bởi vì bạn đang thêm hai dict_items đối tượng với nhau, không phải hai danh sách -

>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

và bạn sẽ phải tạo chúng một cách rõ ràng dưới dạng danh sách, ví dụ: z = dict(list(x.items()) + list(y.items())). Đây là một sự lãng phí tài nguyên và sức mạnh tính toán.

Tương tự, lấy liên minh items()trong Python 3 (viewitems() trong Python 2.7) cũng sẽ thất bại khi các giá trị là các đối tượng không thể thay đổi (ví dụ như các danh sách). Ngay cả khi giá trị của bạn có thể băm, vì các tập hợp không theo thứ tự ngữ nghĩa, hành vi không được xác định liên quan đến quyền ưu tiên. Vì vậy, không làm điều này:

>>> c = dict(a.items() | b.items())

Ví dụ này cho thấy điều gì xảy ra khi các giá trị không thể thực hiện được:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Đây là một ví dụ trong đó y phải được ưu tiên, nhưng thay vào đó giá trị từ x được giữ lại do thứ tự tùy ý của các bộ:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

Một hack bạn không nên sử dụng:

z = dict(x, **y)

Điều này sử dụng dict constructor, và rất nhanh và bộ nhớ hiệu quả (thậm chí nhiều hơn so với quy trình hai bước của chúng tôi) nhưng trừ khi bạn biết chính xác những gì đang xảy ra ở đây (nghĩa là, lệnh thứ hai được chuyển thành đối số từ khóa cho hàm tạo dict), rất khó đọc, nó không phải là mục đích sử dụng, và vì vậy nó không phải là Pythonic.

Đây là một ví dụ về cách sử dụng remediated trong django.

Dicts được thiết kế để lấy các khóa có thể bẻ khóa (ví dụ: hàng chục hoặc tuple), nhưng phương pháp này không thành công trong Python 3 khi các phím không phải là chuỗi.

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

Từ danh sách gửi thư, Guido van Rossum, tác giả của ngôn ngữ, đã viết:

Tôi ổn với   tuyên bố dict ({}, ** {1: 3}) bất hợp pháp, vì sau khi tất cả là lạm dụng   cơ chế.

Dict rõ ràng (x, ** y) là đi xung quanh như là "mát mẻ hack" cho "cuộc gọi   x.update (y) và trả về x ". Cá nhân tôi thấy nó đáng khinh hơn   mát mẻ.

Đó là sự hiểu biết của tôi (cũng như sự hiểu biết về tác giả của ngôn ngữ) rằng mục đích sử dụng cho dict(**y) dành cho việc tạo các dicts cho mục đích dễ đọc, ví dụ:

dict(a=1, b=10, c=11)

thay vì

{'a': 1, 'b': 10, 'c': 11}

Trả lời nhận xét

Mặc dù Guido nói gì, dict(x, **y) phù hợp với đặc tả dict, mà btw. hoạt động cho cả Python 2 và 3. Thực tế là điều này chỉ hoạt động đối với các khóa chuỗi là một hệ quả trực tiếp của các tham số từ khóa hoạt động như thế nào và không phải là một sự ngắn gọn của dict. Cũng không phải là sử dụng toán tử ** ở nơi này một sự lạm dụng của cơ chế, trên thực tế ** được thiết kế chính xác để truyền các dicts như các từ khóa.

Một lần nữa, nó không hoạt động cho 3 khi các phím không phải là chuỗi. Các hợp đồng gọi ngầm là không gian tên có dicts bình thường, trong khi người dùng chỉ phải vượt qua đối số từ khóa đó là chuỗi. Tất cả các cuộc gọi khác đều thực thi nó. dict đã phá vỡ sự nhất quán này trong Python 2:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

Sự không nhất quán này là xấu cho các triển khai khác của Python (Pypy, Jython, IronPython). Vì vậy, nó đã được cố định trong Python 3, vì việc sử dụng này có thể là một thay đổi phá vỡ.

Tôi gửi cho bạn rằng nó là bất lực độc hại để cố ý viết mã mà chỉ hoạt động trong một phiên bản của một ngôn ngữ hoặc chỉ hoạt động được một số ràng buộc tùy ý.

Một bình luận khác:

dict(x.items() + y.items()) vẫn là giải pháp dễ đọc nhất đối với Python 2. Tính khả dụng.

Câu trả lời của tôi: merge_two_dicts(x, y) thực sự có vẻ rõ ràng hơn với tôi, nếu chúng ta thực sự lo ngại về khả năng đọc. Và nó không tương thích về phía trước, vì Python 2 ngày càng bị phản đối.

Trình diễn ít hơn nhưng đúng quảng cáo-hocs

Những cách tiếp cận này ít hiệu quả hơn, nhưng chúng sẽ cung cấp hành vi đúng đắn. Họ sẽ được ít hơn nhiều biểu diễn copy và update hoặc giải nén mới vì chúng lặp qua từng cặp khóa-giá trị ở mức trừu tượng cao hơn, nhưng chúng làm tôn trọng thứ tự ưu tiên (những người yêu cầu sau này có quyền ưu tiên)

Bạn cũng có thể chuỗi các dicts theo cách thủ công bên trong một hiểu dict:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

hoặc trong python 2.6 (và có lẽ sớm nhất là 2.4 khi các biểu thức máy phát điện được giới thiệu):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain sẽ chuỗi các vòng lặp trên các cặp khóa-giá trị theo thứ tự chính xác:

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

Phân tích hiệu suất

Tôi sẽ chỉ thực hiện phân tích hiệu suất của các tập quán được biết là hành xử chính xác.

import timeit

Sau đây được thực hiện trên Ubuntu 14.04

Trong Python 2.7 (Python hệ thống):

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

Trong Python 3.5 (deadsnakes PPA):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

Tài nguyên về Từ điển


3352
2017-11-10 22:11





Trong trường hợp của bạn, những gì bạn có thể làm là:

z = dict(x.items() + y.items())

Điều này sẽ, như bạn muốn nó, đặt dict cuối cùng trong zvà tạo giá trị cho khóa b được ghi đè đúng cách bởi lần thứ hai (y) giá trị của dict:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

Nếu bạn sử dụng Python 3, nó chỉ phức tạp hơn một chút. Để tạo z:

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

1440
2017-09-02 07:50





Một thay thế:

z = x.copy()
z.update(y)

547
2017-09-02 13:00



Để làm rõ lý do tại sao điều này không đáp ứng các critera được cung cấp bởi câu hỏi: nó không phải là một biểu thức duy nhất và nó không trở lại z. - Alex
@ Đôi khi câu hỏi ban đầu không phải là câu hỏi đúng. Ví dụ, nếu bạn cần sử dụng một con quái vật không phải là pythonic của một biểu thức chính quy, hãy làm điều gì đó trong một dòng, hoặc hai dòng thanh lịch. để làm X, sau đó sử dụng hai dòng. Các {**x, **y} cách tiếp cận trong câu trả lời được chấp nhận là một quái vật. - neuronet
@neuronet mỗi oneliner thường chỉ di chuyển mã mà phải xảy ra thành một thành phần khác và giải quyết nó ở đó. đây chắc chắn là một trong những trường hợp. nhưng các ngôn ngữ khác có cấu trúc đẹp hơn python cho việc này. và có một biến thể tham chiếu trong suốt trả về phần tử của nó là một điều tốt đẹp để có điều. - Alex
Đặt nó theo cách này: nếu bạn cần đặt hai dòng chú thích giải thích một dòng mã của bạn cho những người bạn đưa mã của bạn ra ... bạn có thực sự thực hiện nó trong một dòng không? :) Tôi hoàn toàn đồng ý Python không tốt cho điều này: cần có một cách dễ dàng hơn nhiều. Trong khi câu trả lời này là nhiều hơn pythonic, là nó thực sự tất cả rõ ràng hay rõ ràng? Update không phải là một trong những chức năng "cốt lõi" mà mọi người có xu hướng sử dụng nhiều. - neuronet


Một tùy chọn khác ngắn gọn hơn:

z = dict(x, **y)

chú thích: điều này đã trở thành một câu trả lời phổ biến, nhưng điều quan trọng là chỉ ra rằng nếu y có bất kỳ khóa không phải là chuỗi nào, thực tế là công việc này hoàn toàn là sự lạm dụng chi tiết triển khai CPython và nó không hoạt động trong Python 3, hoặc trong PyPy, IronPython hoặc Jython. Cũng thế, Guido không phải là một fan hâm mộ. Vì vậy, tôi không thể đề xuất kỹ thuật này cho mã di động tương thích về phía trước hoặc thực thi chéo, điều này thực sự có nghĩa là nó nên tránh hoàn toàn.


273
2017-09-02 15:52





Điều này có lẽ sẽ không phải là một câu trả lời phổ biến, nhưng bạn gần như chắc chắn không muốn làm điều này. Nếu bạn muốn một bản sao hợp nhất, sau đó sử dụng bản sao (hoặc sâu sắc, tùy thuộc vào những gì bạn muốn) và sau đó cập nhật. Hai dòng mã có thể đọc được nhiều hơn - nhiều Pythonic hơn là tạo dòng đơn với .items () + .items (). Rõ ràng là tốt hơn là ngầm.

Ngoài ra, khi bạn sử dụng .items () (trước Python 3.0), bạn đang tạo một danh sách mới chứa các mục từ dict. Nếu từ điển của bạn là lớn, thì đó là khá nhiều chi phí (hai danh sách lớn sẽ bị vứt bỏ ngay sau khi dict sáp nhập được tạo ra). update () có thể hoạt động hiệu quả hơn, bởi vì nó có thể chạy qua mục nhị phân từng mục.

Về mặt thời gian:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

IMO sự chậm lại nhỏ giữa hai đầu tiên là giá trị nó cho dễ đọc. Ngoài ra, các đối số từ khóa để tạo từ điển chỉ được thêm vào trong Python 2.3, trong khi copy () và update () sẽ hoạt động trong các phiên bản cũ hơn.


168
2017-09-08 11:16





Trong một câu trả lời tiếp theo, bạn đã hỏi về hiệu suất tương đối của hai lựa chọn sau:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

Trên máy của tôi, ít nhất (một x86_64 khá bình thường chạy Python 2.5.2), thay thế z2 không chỉ ngắn hơn và đơn giản hơn mà còn nhanh hơn đáng kể. Bạn có thể xác minh điều này cho chính mình bằng cách sử dụng timeit mô-đun đi kèm với Python.

Ví dụ 1: các từ điển giống hệt nhau ánh xạ 20 số nguyên liên tiếp cho chính chúng:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2 thắng bằng hệ số 3,5 hoặc hơn. Các từ điển khác nhau dường như mang lại kết quả khá khác nhau, nhưng z2 luôn luôn có vẻ đi ra phía trước. (Nếu bạn nhận được kết quả không phù hợp cho tương tự kiểm tra, thử đi qua -r với số lớn hơn số mặc định 3.)

Ví dụ 2: từ điển không chồng chéo ánh xạ 252 chuỗi ngắn đến số nguyên và ngược lại:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 thắng bằng một yếu tố là 10. Đó là một chiến thắng khá lớn trong cuốn sách của tôi!

Sau khi so sánh hai điều đó, tôi tự hỏi liệu z1Hiệu suất kém có thể là do chi phí xây dựng hai danh sách các mục, từ đó dẫn tôi đến tự hỏi liệu biến thể này có thể hoạt động tốt hơn không:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

Một vài bài kiểm tra nhanh, ví dụ:

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

dẫn tôi đến kết luận rằng z3 hơi nhanh hơn z1, nhưng không nhanh bằng z2. Chắc chắn không có giá trị tất cả các đánh máy thêm.

Thảo luận này vẫn còn thiếu một cái gì đó quan trọng, đó là một so sánh hiệu suất của các lựa chọn thay thế với cách "rõ ràng" của việc sáp nhập hai danh sách: bằng cách sử dụng update phương pháp. Để cố gắng giữ cho mọi thứ trên cơ sở bình đẳng với các biểu thức, không cái nào trong số đó sửa đổi x hoặc y, tôi sẽ tạo một bản sao của x thay vì sửa đổi nó tại chỗ, như sau:

z0 = dict(x)
z0.update(y)

Một kết quả điển hình:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

Nói cách khác, z0 và z2 dường như có hiệu suất cơ bản giống hệt nhau. Bạn có nghĩ đây có phải là sự trùng hợp không? Tôi không....

Trong thực tế, tôi muốn đi xa như vậy để tuyên bố rằng nó không thể cho mã Python tinh khiết để làm bất kỳ tốt hơn này. Và nếu bạn có thể làm tốt hơn đáng kể trong một mô-đun mở rộng C, tôi tưởng tượng các folks Python cũng có thể quan tâm đến việc kết hợp mã của bạn (hoặc một biến thể về cách tiếp cận của bạn) vào lõi Python. Sử dụng Python dict ở nhiều nơi; tối ưu hóa hoạt động của nó là một việc lớn.

Bạn cũng có thể viết như

z0 = x.copy()
z0.update(y)

như Tony, nhưng (không ngạc nhiên) sự khác biệt trong ký hiệu hóa ra không có bất kỳ hiệu ứng đo lường nào về hiệu suất. Sử dụng cái nào phù hợp với bạn. Tất nhiên, anh ấy hoàn toàn chính xác để chỉ ra rằng phiên bản hai câu lệnh dễ hiểu hơn nhiều.


116
2017-10-23 02:38



Điều này không hoạt động trong Python 3; items() không phải là có thể thay đổi, và iteritems không tồn tại. - Antti Haapala


Tôi muốn một cái gì đó tương tự, nhưng với khả năng xác định như thế nào các giá trị trên các phím trùng lặp đã được sáp nhập, vì vậy tôi hacked này ra (nhưng đã không kiểm tra rất nhiều nó). Rõ ràng đây không phải là một biểu thức duy nhất, nhưng nó là một cuộc gọi hàm duy nhất.

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

86
2017-09-04 19:08





Trong Python 3, bạn có thể sử dụng collections.ChainMap nhóm nhiều dicts hoặc các ánh xạ khác lại với nhau để tạo một chế độ xem đơn lẻ, có thể cập nhật:

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = ChainMap({}, y, x)
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

73
2018-04-28 03:15



Nhưng một trong những nên thận trọng trong khi sử dụng ChainMap có một bắt rằng nếu bạn có các phím trùng lặp các giá trị từ bản đồ đầu tiên được sử dụng và khi bạn gọi một del nói rằng một ChainMap c sẽ xóa ánh xạ đầu tiên của khóa đó. - Prerit
@Prerit Bạn còn mong đợi điều gì khác nữa? Đó là cách làm việc không gian tên chuỗi bình thường. Hãy xem xét làm thế nào $ PATH hoạt động trong bash. Việc xóa một tệp thực thi trên đường dẫn không loại trừ một tệp thực thi khác có cùng tên ngược dòng. - Raymond Hettinger
@ Raymond Hettinger Tôi đồng ý, chỉ cần thêm một cảnh báo. Hầu hết mọi người có thể không biết về nó. : D - Prerit
Tôi đến đây để giới thiệu sự đơn giản của collections.ChainMap. - hughdbrown
Trên thực tế, loại đầu ra bạn có ở đây có thể đạt được ngay cả khi không sử dụng ChainMap đúng? Ý tôi là, tất cả những gì bạn phải làm chỉ là cập nhật z = {**x, **y} và làm theo các bước truyền thống, đó là nó! - Steffi Keran Rani J


Đệ quy / cập nhật sâu một dict

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

Trình diễn:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

Đầu ra:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

Cảm ơn rednaw cho chỉnh sửa.


61
2017-11-29 11:52





Phiên bản tốt nhất mà tôi có thể nghĩ khi không sử dụng bản sao sẽ là:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

Nó nhanh hơn dict(x.items() + y.items()) nhưng không nhanh như n = copy(a); n.update(b), ít nhất là trên CPython. Phiên bản này cũng hoạt động trong Python 3 nếu bạn thay đổi iteritems() đến items(), được tự động thực hiện bởi công cụ 2to3.

Cá nhân tôi thích phiên bản này tốt nhất vì nó mô tả khá tốt những gì tôi muốn trong một cú pháp chức năng duy nhất. Vấn đề nhỏ duy nhất là nó không làm cho hoàn toàn rõ ràng rằng các giá trị từ y được ưu tiên hơn các giá trị từ x, nhưng tôi không tin rằng thật khó để tìm ra điều đó.


54
2017-10-14 18:55





Python 3.5 (PEP 448) cho phép tùy chọn cú pháp đẹp hơn:

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

Hoặc thậm chí

final = {'a': 1, 'b': 1, **x, **y}

41
2018-02-26 21:27



Giải pháp này tốt hơn so với cách nào dict(x, **y)-dung dịch? Khi bạn (@CarlMeyer) được đề cập trong phần ghi chú câu trả lời của riêng bạn (stackoverflow.com/a/39858/2798610) Guido xem xét giải pháp đó bất hợp pháp. - Blackeagle52
Guido không thích dict(x, **y) vì lý do (rất tốt) mà nó dựa vào y chỉ có các khóa là các tên đối số từ khóa hợp lệ (trừ khi bạn đang sử dụng CPython 2.7, nơi cheat constructor dict). Phản đối / hạn chế này không áp dụng cho PEP 448, tổng quát hóa ** giải nén cú pháp để dict literals. Vì vậy, giải pháp này có cùng một vết cắt như dict(x, **y), không có nhược điểm. - Carl Meyer