ฉันจะรวมพจนานุกรมเข้าด้วยกันใน Python ได้อย่างไร


91
d3 = dict(d1, **d2)

ฉันเข้าใจว่าสิ่งนี้รวมพจนานุกรม แต่มันไม่เหมือนใคร? จะเกิดอะไรขึ้นถ้า d1 มีคีย์เดียวกันกับ d2 แต่มีค่าต่างกัน ฉันต้องการรวม d1 และ d2 แต่ d1 มีลำดับความสำคัญหากมีคีย์ที่ซ้ำกัน


9
โปรดทราบว่าเคล็ดลับนี้ถือเป็นการใช้**อาร์กิวเมนต์ของคีย์เวิร์ดในทางที่ผิดเว้นแต่คีย์ทั้งหมดd2จะเป็นสตริง หากไม่ใช่คีย์ทั้งหมดของd2สตริงสิ่งนี้จะล้มเหลวใน Python 3.2 และในการใช้ Python ทางเลือกเช่น Jython, IronPython และ PyPy ดูตัวอย่างเช่นmail.python.org/pipermail/python-dev/2010-April/099459.html
Mark Dickinson

คำตอบ:


155

คุณสามารถใช้.update()วิธีนี้หากคุณไม่ต้องการต้นฉบับd2อีกต่อไป:

อัพเดทพจนานุกรมกับคู่ค่า / สำคัญจากอื่น ๆคีย์เขียนทับที่มีอยู่ กลับNone.

เช่น:

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

อัปเดต:

แน่นอนคุณสามารถคัดลอกพจนานุกรมก่อนเพื่อสร้างใหม่ที่ผสาน สิ่งนี้อาจจำเป็นหรือไม่ก็ได้ ในกรณีที่คุณมีวัตถุผสม (วัตถุที่มีวัตถุอื่น ๆ เช่นรายการหรืออินสแตนซ์คลาส) ในพจนานุกรมของคุณcopy.deepcopyควรได้รับการพิจารณาด้วย


1
ด้วยกรณีนี้องค์ประกอบ d1 ควรได้รับลำดับความสำคัญอย่างถูกต้องหากพบคีย์ที่ขัดแย้งกัน
Trey Hunner

ในกรณีที่คุณยังต้องการเพียงแค่ทำสำเนา d3 = d2.copy () d3.update (d1) แต่ฉันต้องการเห็น d1 + d2 ถูกเพิ่มในภาษา
stach

4
d1 + d2 เป็นปัญหาเนื่องจากพจนานุกรมหนึ่งรายการต้องมีลำดับความสำคัญระหว่างความขัดแย้งและไม่ชัดเจนว่าอันไหน
rjh

d1 + d2 จะใช้งานได้ก็ต่อเมื่อ Python ได้รับมัลติแมปมิฉะนั้นความคลุมเครือของผู้ใช้จะทำให้ผู้ใช้สับสนเกินไปสำหรับอัตราขยายการพิมพ์ 8 ไบต์
Nick Bastin

คุณมีวัตถุในพจนานุกรมในตัวอย่างนี้isinstance(int, object) is Trueแต่deepcopyดูเหมือนจะไม่จำเป็น
Antony Hatchkins

43

ใน Python2

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

d1 แทนที่ d2:

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

d2 แทนที่ d1:

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

พฤติกรรมนี้ไม่ใช่แค่ความบังเอิญในการนำไปปฏิบัติเท่านั้น มีการรับประกันในเอกสารประกอบ :

หากระบุคีย์ทั้งในอาร์กิวเมนต์ตำแหน่งและอาร์กิวเมนต์คำหลักค่าที่เกี่ยวข้องกับคีย์เวิร์ดจะถูกเก็บไว้ในพจนานุกรม


3
ตัวอย่างของคุณจะล้มเหลว (สร้าง TypeError) ใน Python 3.2 และในเวอร์ชันปัจจุบันของ Jython, PyPy และ IronPython: สำหรับ Python เวอร์ชันเหล่านั้นเมื่อส่งคำสั่งด้วย**สัญลักษณ์คีย์ทั้งหมดของ dict นั้นควรเป็นสตริง ดูเธรด python-dev ที่เริ่มต้นที่mail.python.org/pipermail/python-dev/2010-April/099427.htmlสำหรับข้อมูลเพิ่มเติม
Mark Dickinson

@ มาร์ค: ขอบคุณสำหรับหัว ฉันได้แก้ไขโค้ดเพื่อให้เข้ากันได้กับการใช้งานที่ไม่ใช่ CPython
unutbu

3
มันจะล้มเหลวหากคีย์ของคุณเป็นสิ่งที่รวมกันของสตริงและตัวเลข เช่น d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy

เกี่ยวกับไวยากรณ์การคลายแพ็กดูโพสต์นี้สำหรับการเปลี่ยนแปลงที่จะเกิดขึ้นใน python 3.5
Ioannis Filippidis

ฉันจะบอกว่าได้d = dict(**d1, **d2)ผล แต่นั่นคือสิ่งที่ @IoannisFilippidis อ้างอิงในความคิดเห็นของพวกเขา บางทีการรวมตัวอย่างข้อมูลไว้ที่นี่จะชัดเจนกว่าดังนั้นนี่คือ
dwanderson

14

หากคุณต้องการd1มีลำดับความสำคัญในความขัดแย้งให้ทำ:

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

มิฉะนั้นย้อนกลับและd2d1


1

วิธีแก้ปัญหาของฉันคือกำหนดฟังก์ชันการผสาน ไม่ซับซ้อนและเสียค่าใช้จ่ายเพียงบรรทัดเดียว นี่คือรหัสใน 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()) }

การทดสอบ

>>> 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()
{}

ใช้ได้กับอาร์กิวเมนต์ในพจนานุกรมตามจำนวนที่กำหนด หากมีคีย์ที่ซ้ำกันในพจนานุกรมเหล่านั้นคีย์จากพจนานุกรมด้านขวาสุดในรายการอาร์กิวเมนต์จะชนะ


1
การวนซ้ำแบบธรรมดาที่มี.updateสายเรียกเข้า ( merged={}ตามด้วยfor d in dict: merged.update(d)) จะสั้นลงอ่านง่ายขึ้นและมีประสิทธิภาพมากขึ้น
Mark Dickinson

1
หรือถ้าอยากใช้จริงๆreduceแล้วlambdas ยังไงreturn reduce(lambda x, y: x.update(y) or x, dicts, {})?
Mark Dickinson

1
คุณสามารถลองใช้รหัสของคุณในเชลล์และดูว่าถูกต้องหรือไม่ สิ่งที่ฉันพยายามทำคือการเขียนฟังก์ชันที่สามารถรับอาร์กิวเมนต์ในพจนานุกรมจำนวนต่างๆด้วยฟังก์ชันเดียวกัน จะเป็นการดีกว่าที่จะไม่ใช้ x.update (y) ใต้แลมบ์ดาเพราะจะส่งกลับค่าNoneเสมอ และฉันกำลังพยายามเขียนmerge_ฟังก์ชันทั่วไปที่ใช้อาร์กิวเมนต์พจนานุกรมจำนวนมากและจัดการกับคีย์ที่ซ้ำกันด้วยฟังก์ชันที่ให้มา เมื่อฉันทำเสร็จแล้วฉันจะโพสต์ในเธรดอื่นซึ่งวิธีแก้ปัญหามีความเกี่ยวข้องมากกว่า
Lei Zhao

นี่คือลิงค์ที่ฉันเขียนวิธีแก้ปัญหาทั่วไป ยินดีต้อนรับและดู
Lei Zhao


1

เริ่มต้นในPython 3.9โอเปอเรเตอร์|จะสร้างพจนานุกรมใหม่โดยใช้คีย์และค่าที่ผสานจากสองพจนานุกรม:

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

นี้:

สร้างพจนานุกรม d3 ใหม่ด้วยคีย์และค่าที่ผสานของ d2 และ d1 ค่าของ d1 มีลำดับความสำคัญเมื่อ d2 และ d1 แชร์คีย์


โปรดสังเกตตัว|=ดำเนินการที่แก้ไข d2 โดยการรวม d1 เข้าโดยให้ลำดับความสำคัญกับค่า d1:

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


0

ฉันเชื่อว่าตามที่ระบุไว้ข้างต้นการใช้d2.update(d1)เป็นแนวทางที่ดีที่สุดและคุณสามารถคัดลอกd2ก่อนได้หากยังต้องการ

แม้ว่าฉันต้องการชี้ให้เห็นว่าdict(d1, **d2)เป็นวิธีที่ไม่ดีในการรวมพจนานุกรมโดยทั่วไปเนื่องจากอาร์กิวเมนต์คำหลักจำเป็นต้องเป็นสตริงดังนั้นจึงจะล้มเหลวหากคุณมีdictเช่น:

{
  1: 'foo',
  2: 'bar'
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.