ฉันจะรวมพจนานุกรม Python สองพจนานุกรมเข้าด้วยกันในนิพจน์เดียวได้อย่างไร
สำหรับพจนานุกรมx
และy
, z
กลายเป็นตื้นรวมพจนานุกรมที่มีค่าจากการเปลี่ยนจากy
x
ใน Python 3.5 หรือสูงกว่า:
z = {**x, **y}
ใน Python 2 (หรือ 3.4 หรือต่ำกว่า) เขียนฟังก์ชัน:
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)
ในหลาม 3.9.0a4 หรือมากกว่าวันที่ (รุ่นสุดท้ายประมาณตุลาคม 2020): PEP-584 , กล่าวถึงที่นี่ได้รับการดำเนินการเพื่อให้ง่ายต่อไปนี้:
z = x | y # NOTE: 3.9+ ONLY
คำอธิบาย
สมมติว่าคุณมีสอง dicts และคุณต้องการรวมมันเข้าไปใน dict ใหม่โดยไม่ต้องเปลี่ยน dicts ดั้งเดิม:
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
ผลลัพธ์ที่ต้องการคือการรับพจนานุกรมใหม่ ( z
) ด้วยค่าที่ผสานและค่าของ dict ที่สองเขียนทับสิ่งเหล่านั้นตั้งแต่แรก
>>> z
{'a': 1, 'b': 3, 'c': 4}
ไวยากรณ์ใหม่สำหรับสิ่งนี้ซึ่งเสนอในPEP 448และมีให้ตั้งแต่ Python 3.5คือ
z = {**x, **y}
และมันก็เป็นเพียงนิพจน์เดียว
โปรดทราบว่าเราสามารถผสานด้วยสัญกรณ์ตามตัวอักษรได้เช่นกัน:
z = {**x, 'foo': 1, 'bar': 2, **y}
และตอนนี้:
>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}
ตอนนี้มันแสดงให้เห็นว่ามีการใช้งานในกำหนดการวางตลาดสำหรับ 3.5, PEP 478และตอนนี้ได้นำมาสู่สิ่งใหม่ในเอกสารPython 3.5
อย่างไรก็ตามเนื่องจากหลายองค์กรยังคงใช้งาน Python 2 อยู่คุณอาจต้องการทำสิ่งนี้ด้วยวิธีที่เข้ากันได้แบบย้อนหลัง วิธี Pythonic แบบคลาสสิกที่มีอยู่ใน Python 2 และ Python 3.0-3.4 คือการทำเช่นนี้เป็นกระบวนการสองขั้นตอน:
z = x.copy()
z.update(y) # which returns None since it mutates z
ในทั้งสองวิธีy
จะมาที่สองและค่าของมันจะแทนที่x
ค่าของดังนั้นจึง'b'
จะชี้ไปที่3
ผลลัพธ์สุดท้ายของเรา
ยังไม่ได้ใช้ Python 3.5 แต่ต้องการนิพจน์เดียว
หากคุณยังไม่ได้ใช้ Python 3.5 หรือต้องการเขียนโค้ดที่เข้ากันได้แบบย้อนหลังและคุณต้องการสิ่งนี้ในการแสดงออกครั้งเดียวผู้ที่มีประสิทธิภาพมากที่สุดในขณะที่วิธีการที่ถูกต้องคือการใส่มันลงในฟังก์ชั่น:
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
แล้วคุณมีนิพจน์เดียว:
z = merge_two_dicts(x, y)
คุณสามารถสร้างฟังก์ชันเพื่อรวมจำนวน dicts ที่ไม่ได้กำหนดจากศูนย์ถึงจำนวนมาก:
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
ฟังก์ชั่นนี้จะทำงานใน Python 2 และ 3 สำหรับ dicts ทั้งหมด เช่นให้ dicts a
กับg
:
z = merge_dicts(a, b, c, d, e, f, g)
และคู่ของค่าคีย์g
จะมีความสำคัญมากกว่า dicts a
ถึงf
และอื่น ๆ
คำวิจารณ์ของคำตอบอื่น ๆ
อย่าใช้สิ่งที่คุณเห็นในคำตอบที่ยอมรับก่อนหน้านี้:
z = dict(x.items() + y.items())
ใน Python 2 คุณสร้างสองรายการในหน่วยความจำสำหรับแต่ละ dict สร้างรายการที่สามในหน่วยความจำที่มีความยาวเท่ากับความยาวของสองรายการแรกที่รวมกันแล้วละทิ้งรายการทั้งสามเพื่อสร้าง dict ใน Python 3 สิ่งนี้จะล้มเหลวเพราะคุณกำลังเพิ่มdict_items
วัตถุสองชิ้นเข้าด้วยกันไม่ใช่สองรายการ -
>>> 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'
z = dict(list(x.items()) + list(y.items()))
และคุณจะต้องชัดเจนสร้างพวกเขาเป็นรายการเช่น นี่เป็นการสิ้นเปลืองทรัพยากรและอำนาจในการคำนวณ
ในทำนองเดียวกันการรวมกันของitems()
ใน Python 3 ( viewitems()
ใน Python 2.7) จะล้มเหลวเมื่อค่าเป็นวัตถุที่ไม่สามารถล้างได้ (เช่นรายการเช่น) แม้ว่าค่าของคุณจะแฮชได้เนื่องจากชุดนั้นไม่ได้เรียงลำดับความหมาย แต่พฤติกรรมนั้นไม่ได้ถูกกำหนดโดยคำนึงถึงลำดับความสำคัญ ดังนั้นอย่าทำสิ่งนี้:
>>> c = dict(a.items() | b.items())
ตัวอย่างนี้แสดงให้เห็นถึงสิ่งที่เกิดขึ้นเมื่อค่า unhashable:
>>> 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 ควรมีลำดับความสำคัญ แต่แทนที่จะเก็บค่าจาก x เนื่องจากลำดับของชุดโดยพลการ:
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}
แฮ็คอื่นที่คุณไม่ควรใช้:
z = dict(x, **y)
สิ่งนี้ใช้ตัวdict
สร้างและรวดเร็วมากและมีประสิทธิภาพของหน่วยความจำ (ยิ่งกว่าเล็กน้อยดังนั้นมากกว่ากระบวนการสองขั้นตอนของเรา) แต่หากคุณไม่ทราบว่าเกิดอะไรขึ้นที่นี่ คอนสตรัคเตอร์) มันยากที่จะอ่านไม่ใช่การใช้ที่ตั้งใจและไม่ Pythonic
นี่คือตัวอย่างของการใช้งานที่ถูกremediated ใน Django
Dicts มีจุดประสงค์เพื่อใช้คีย์ hashable (เช่น frozensets หรือ tuples) แต่วิธีนี้ล้มเหลวใน Python 3 เมื่อคีย์ไม่ใช่สตริง
>>> c = dict(a, **b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
จากรายชื่อผู้รับจดหมาย Guido van Rossum ผู้สร้างภาษาเขียนว่า:
ฉันสบายดีกับการประกาศ dict ({}, ** {1: 3}) ที่ผิดกฎหมายเพราะหลังจากนั้นมันเป็นการละเมิดกลไก **
และ
เห็นได้ชัดว่า dict (x, ** y) กำลังดำเนินไปในลักษณะ "cool hack" สำหรับ "call x.update (y) และ return x" โดยส่วนตัวแล้วฉันพบว่ามันน่ารังเกียจยิ่งกว่าเท่ห์
มันเป็นความเข้าใจของฉัน (รวมถึงความเข้าใจของผู้สร้างภาษา ) ที่การใช้งานที่ตั้งใจไว้dict(**y)
สำหรับการสร้าง dicts เพื่อวัตถุประสงค์ในการอ่านเช่น:
dict(a=1, b=10, c=11)
แทน
{'a': 1, 'b': 10, 'c': 11}
ตอบสนองต่อความคิดเห็น
แม้จะมีสิ่งที่กุยโด้กล่าวว่าdict(x, **y)
เป็นไปตามข้อกำหนด dict ซึ่ง btw ใช้งานได้กับทั้ง Python 2 และ 3 ข้อเท็จจริงที่ว่าการทำงานเฉพาะกับคีย์สตริงนั้นเป็นผลโดยตรงจากการทำงานของพารามิเตอร์คำหลักและไม่ใช่การใช้คำสั่งสั้น ๆ หรือใช้ตัวดำเนินการ ** ในที่นี้เป็นการละเมิดกลไกในความเป็นจริง ** ได้รับการออกแบบมาอย่างแม่นยำเพื่อส่งผ่านการกำหนดเป็นคำหลัก
อีกครั้งมันไม่ทำงานสำหรับ 3 เมื่อคีย์ไม่ใช่สตริง สัญญาการโทรโดยนัยคือเนมสเปซใช้ dicts สามัญในขณะที่ผู้ใช้จะต้องส่งผ่านอาร์กิวเมนต์คำหลักที่เป็นสตริงเท่านั้น callables อื่นทั้งหมดบังคับใช้ dict
ทำลายความสอดคล้องนี้ใน 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}
ความไม่สอดคล้องกันนี้ไม่ดีเนื่องจากการนำ Python ไปใช้งานแบบอื่น (Pypy, Jython, IronPython) ดังนั้นจึงได้รับการแก้ไขใน Python 3 เนื่องจากการใช้งานนี้อาจเป็นการเปลี่ยนแปลงที่ไม่สมบูรณ์
ฉันส่งให้คุณว่ามันเป็นการไร้ความสามารถที่เป็นอันตรายในการเขียนโค้ดโดยเจตนาซึ่งใช้งานได้ในภาษาหนึ่งเวอร์ชันเท่านั้นหรือใช้งานได้เพียงบางข้อเท่านั้น
ความคิดเห็นเพิ่มเติม:
dict(x.items() + y.items())
ยังคงเป็นโซลูชันที่อ่านได้มากที่สุดสำหรับ Python 2 จำนวนการอ่านได้
การตอบสนองของฉัน: merge_two_dicts(x, y)
จริง ๆ แล้วดูเหมือนชัดเจนสำหรับฉันถ้าเรากังวลเกี่ยวกับการอ่าน และมันไม่รองรับการใช้งานในอนาคตเนื่องจาก Python 2 เลิกใช้งานมากขึ้น
{**x, **y}
ดูเหมือนจะไม่จัดการพจนานุกรมที่ซ้อนกัน เนื้อหาของปุ่มที่ซ้อนกันนั้นเขียนทับง่ายไม่รวม [... ] ฉันสิ้นสุดการถูกเผาโดยคำตอบเหล่านี้ที่ไม่รวมการเรียกซ้ำและฉันรู้สึกประหลาดใจที่ไม่มีใครพูดถึงมัน ในการตีความของฉันของคำว่า "การรวม" คำตอบเหล่านี้อธิบาย "ปรับปรุงหนึ่ง dict กับอีก" และไม่รวม
ใช่. ผมต้องดูให้คุณกลับไปที่คำถามซึ่งจะขอตื้นผสานสองพจนานุกรมมีค่าเป็นครั้งแรกที่ถูกเขียนทับโดยที่สอง - ในการแสดงออกเดียว
สมมติว่าพจนานุกรมสองพจนานุกรมหนึ่งพจนานุกรมหนึ่งอาจรวมเข้าด้วยกันในฟังก์ชันเดียว แต่คุณควรระวังไม่แก้ไข dicts จากแหล่งใดแหล่งหนึ่งและวิธีที่แน่นอนที่สุดเพื่อหลีกเลี่ยงการทำสำเนาเมื่อทำการกำหนดค่า เนื่องจากคีย์ต้อง hashable และมักจะไม่เปลี่ยนรูปดังนั้นจึงไม่มีประโยชน์ที่จะคัดลอก:
from copy import deepcopy
def dict_of_dicts_merge(x, y):
z = {}
overlapping_keys = x.keys() & y.keys()
for key in overlapping_keys:
z[key] = dict_of_dicts_merge(x[key], y[key])
for key in x.keys() - overlapping_keys:
z[key] = deepcopy(x[key])
for key in y.keys() - overlapping_keys:
z[key] = deepcopy(y[key])
return z
การใช้งาน:
>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}
ขึ้นมาพร้อมกับภาระผูกพันสำหรับประเภทค่าอื่น ๆ ที่อยู่ไกลเกินขอบเขตของคำถามนี้ดังนั้นผมจะชี้ให้คุณในคำตอบของฉันไปที่คำถามที่ยอมรับใน "พจนานุกรมพจนานุกรมรวม"
ประสิทธิภาพน้อยลง แต่แก้ไขโฆษณา
วิธีการเหล่านี้มีประสิทธิภาพน้อยกว่า แต่จะให้พฤติกรรมที่ถูกต้อง พวกเขาจะมากน้อย performant กว่าcopy
และupdate
หรือเอาออกใหม่เพราะพวกเขาย้ำผ่านแต่ละคู่สำคัญที่มีมูลค่าในระดับที่สูงขึ้นของนามธรรม แต่พวกเขาทำเคารพคำสั่งของลำดับความสำคัญ (dicts หลังมีความสำคัญ)
นอกจากนี้คุณยังสามารถเชื่อมโยง dicts ด้วยตนเองภายใน dict comprehension:
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
หรือในไพ ธ อน 2.6 (และอาจเร็วเท่า 2.4 เมื่อมีการแนะนำนิพจน์เครื่องกำเนิดไฟฟ้า):
dict((k, v) for d in dicts for k, v in d.items())
itertools.chain
จะโยงตัววนซ้ำไปยังคู่คีย์ - ค่าตามลำดับที่ถูกต้อง:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
การวิเคราะห์ประสิทธิภาพ
ฉันแค่จะทำการวิเคราะห์ประสิทธิภาพของประเพณีที่รู้จักกันว่าทำงานอย่างถูกต้อง
import timeit
สิ่งต่อไปนี้ทำบน Ubuntu 14.04
ใน Python 2.7 (ระบบ Python):
>>> 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
ใน 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
แหล่งข้อมูลเกี่ยวกับพจนานุกรม
z = x | y