Làm cách nào để kết hợp các từ điển với nhau bằng Python?


90
d3 = dict(d1, **d2)

Tôi hiểu rằng điều này hợp nhất từ ​​điển. Nhưng, nó có phải là duy nhất? Điều gì sẽ xảy ra nếu d1 có cùng khóa với d2 nhưng khác giá trị? Tôi muốn d1 và d2 được hợp nhất, nhưng d1 được ưu tiên nếu có khóa trùng lặp.


9
Xin lưu ý rằng thủ thuật này được coi là lạm dụng **truyền đối số từ khóa trừ khi tất cả các khóa của d2đều là chuỗi. Nếu không phải tất cả các khóa của d2đều là chuỗi, điều này không thành công trong Python 3.2 và trong các triển khai thay thế của Python như Jython, IronPython và PyPy. Ví dụ: xem mail.python.org/pipermail/python-dev/2010-April/099459.html .
Mark Dickinson

Câu trả lời:


151

Bạn có thể sử dụng .update()phương pháp này nếu không cần bản gốc d2nữa:

Cập nhật từ điển bằng các cặp khóa / giá trị từ các khóa khác, ghi đè các khóa hiện có . Trở lại None.

Ví dụ:

>>> d1 = {'a': 1, 'b': 2} 
>>> d2 = {'b': 1, 'c': 3}
>>> d2.update(d1)
>>> d2
{'a': 1, 'c': 3, 'b': 2}

Cập nhật:

Tất nhiên, bạn có thể sao chép từ điển trước để tạo một từ điển mới được hợp nhất. Điều này có thể cần thiết hoặc không. Trong trường hợp bạn có các đối tượng ghép (các đối tượng chứa các đối tượng khác, như danh sách hoặc cá thể lớp) trong từ điển của mình, copy.deepcopycũng nên được xem xét.


1
Với trường hợp này, các phần tử d1 sẽ được ưu tiên một cách chính xác nếu tìm thấy các khóa xung đột
Trey Hunner

Trong trường hợp bạn vẫn cần nó, chỉ cần tạo một bản sao. d3 = d2.copy () d3.update (d1) nhưng tôi muốn thấy d1 + d2 được thêm vào ngôn ngữ.
stach

4
d1 + d2 có vấn đề vì một từ điển phải được ưu tiên trong các cuộc xung đột và không rõ ràng là từ điển nào.
rjh

d1 + d2 sẽ chỉ được triển khai nếu Python đạt được nhiều bản đồ, nếu không, sự mơ hồ đối với người dùng quá khó hiểu đối với mức tăng nhập 8 byte.
Nick Bastin

Bạn có các đối tượng trong từ điển trong ví dụ này: isinstance(int, object) is Truenhưng deepcopydường như không cần thiết.
Antony Hatchkins

43

Trong Python2,

d1={'a':1,'b':2}
d2={'a':10,'c':3}

d1 ghi đè d2:

dict(d2,**d1)
# {'a': 1, 'c': 3, 'b': 2}

d2 ghi đè d1:

dict(d1,**d2)
# {'a': 10, 'c': 3, 'b': 2}

Hành vi này không chỉ là một sự may mắn khi thực hiện; nó được đảm bảo trong tài liệu :

Nếu một khóa được chỉ định cả trong đối số vị trí và đối số từ khóa, thì giá trị được liên kết với từ khóa sẽ được giữ lại trong từ điển.


3
Các ví dụ của bạn sẽ không thành công (tạo ra TypeError) trong Python 3.2 và trong các phiên bản hiện tại của Jython, PyPy và IronPython: đối với các phiên bản Python đó, khi truyền một lệnh có **ký hiệu, tất cả các khóa của lệnh đó phải là chuỗi. Xem chuỗi python-dev bắt đầu tại mail.python.org/pipermail/python-dev/2010-April/099427.html để biết thêm.
Mark Dickinson

@Mark: Cảm ơn vì sự quan tâm của bạn. Tôi đã chỉnh sửa mã để làm cho nó tương thích với các triển khai không phải CPython.
unutbu

3
nó không thành công nếu các khóa của bạn là nhiều chuỗi và số. ví dụ. d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy

Về cú pháp giải nén, hãy xem bài đăng này để biết các thay đổi sắp tới trong python 3.5.
Ioannis Filippidis

Tôi đã định nói rằng điều đó hiệu d = dict(**d1, **d2)quả, nhưng đó là những gì @IoannisFilippidis tham chiếu trong nhận xét của họ. Có lẽ bao gồm đoạn mã ở đây sẽ rõ ràng hơn, vì vậy nó là ở đây.
dwanderson

14

Nếu bạn muốn d1được ưu tiên trong các cuộc xung đột, hãy làm:

d3 = d2.copy()
d3.update(d1)

Ngược lại, đảo ngược d2d1.


1

Giải pháp của tôi là xác định một hàm hợp nhất . Nó không phức tạp và chỉ tốn một đường. Đây là mã trong Python 3.

from functools import reduce
from operator import or_

def merge(*dicts):
    return { k: reduce(lambda d, x: x.get(k, d), dicts, None) for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) }

Kiểm tra

>>> d = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> d_letters = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d, d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d_letters, d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> merge(d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge()
{}

Nó hoạt động với số lượng đối số từ điển tùy ý. Nếu có bất kỳ khóa trùng lặp nào trong từ điển đó, khóa từ từ điển ngoài cùng bên phải trong danh sách đối số sẽ thắng.


1
Một vòng lặp đơn giản với một .updatecuộc gọi trong đó ( merged={}theo sau là for d in dict: merged.update(d)) sẽ ngắn hơn, dễ đọc hơn và hiệu quả hơn.
Mark Dickinson

1
Hoặc nếu bạn thực sự muốn sử dụng reducelambdas, làm thế nào về return reduce(lambda x, y: x.update(y) or x, dicts, {})?
Mark Dickinson

1
Bạn có thể thử mã của mình trong shell và xem nó có đúng không. Những gì tôi đang cố gắng làm là viết một hàm có thể nhận nhiều đối số từ điển khác nhau với cùng một chức năng. Tốt hơn là không sử dụng x.update (y) dưới lambda, vì nó luôn trả về Không có . Và tôi đang cố gắng viết một hàm merge_with tổng quát hơn lấy nhiều đối số từ điển khác nhau và xử lý các khóa trùng lặp với hàm được cung cấp. Sau khi hoàn tất, tôi sẽ đăng nó trong một chủ đề khác, nơi giải pháp phù hợp hơn.
Lei Zhao

Đây là liên kết nơi tôi đã viết giải pháp chung hơn. Chào mừng và có một cái nhìn.
Lei Zhao


1

Bắt đầu từ Python 3.9, toán tử |tạo một từ điển mới với các khóa và giá trị được hợp nhất từ ​​hai từ điển:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d3 = d2 | d1
# d3: {'b': 2, 'c': 3, 'a': 1}

Điều này:

Tạo một từ điển mới d3 với các khóa và giá trị được hợp nhất của d2 và d1. Các giá trị của d1 được ưu tiên khi d2 và d1 dùng chung khóa.


Cũng lưu ý |=toán tử sửa đổi d2 bằng cách hợp nhất d1 vào, với mức độ ưu tiên trên các giá trị d1:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d2 |= d1
# d2: {'b': 2, 'c': 3, 'a': 1}


0

Tôi tin rằng, như đã nói ở trên, sử dụng d2.update(d1)là cách tốt nhất và bạn cũng có thể sao chép d2trước nếu vẫn cần.

Mặc dù, tôi muốn chỉ ra rằng đó dict(d1, **d2)thực sự là một cách không tốt để hợp nhất các thứ nguyên nói chung vì các đối số của từ khóa cần phải là chuỗi, do đó, nó sẽ không thành công nếu bạn có dictnhư sau:

{
  1: 'foo',
  2: 'bar'
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.