Python อัปเดตคีย์ใน dict หากไม่มีอยู่


109

ฉันต้องการแทรกคู่คีย์ - ค่าลงใน dict ถ้าคีย์ไม่อยู่ใน dict.keys () โดยทั่วไปฉันสามารถทำได้ด้วย:

if key not in d.keys():
    d[key] = value

แต่มีวิธีที่ดีกว่านี้หรือไม่? หรืออะไรคือวิธีแก้ปัญหา pythonic สำหรับปัญหานี้?


2
เป็นไปได้ที่จะซ้ำกันของSet a value ใน dict ก็ต่อเมื่อยังไม่ได้ตั้งค่า
stijn

คำตอบ:


153

คุณไม่จำเป็นต้องโทรd.keys()ดังนั้น

if key not in d:
    d[key] = value

ก็เพียงพอแล้ว ไม่มีวิธีการอ่านที่ชัดเจนกว่านี้

คุณสามารถอัปเดตได้อีกครั้งโดยdict.get()จะคืนค่าที่มีอยู่หากมีคีย์อยู่แล้ว:

d[key] = d.get(key, value)

แต่ฉันขอแนะนำอย่างยิ่งต่อสิ่งนี้ นี่คือการตีกอล์ฟซึ่งขัดขวางการบำรุงรักษาและความสามารถในการอ่าน


ฉันจำเป็นต้องซิงโครไนซ์สิ่งนี้หรือไม่หากฉันมีหลายเธรดโดยใช้รหัส
ed22

1
@ ed22 ใช่เนื่องจากประกอบด้วยคำแนะนำหลาย bytecode ซึ่งระหว่างเธรดสามารถสลับได้
Martijn Pieters

84

ใช้dict.setdefault():

>>> d = {1: 'one'}
>>> d.setdefault(1, '1')
'one'
>>> d    # d has not changed because the key already existed
{1: 'one'}
>>> d.setdefault(2, 'two')
'two'
>>> d
{1: 'one', 2: 'two'}

6
dict.setdefault()ควรใช้เมื่อพยายามเข้าถึงค่าด้วย
Martijn Pieters

14
@MartijnPieters: ทำไมมันถึงสำคัญ? ชื่อนี้setdefault()อธิบายถึงสิ่งที่เกิดขึ้นอย่างชัดเจน - มันยังส่งคืนค่าที่ไม่สำคัญและ IMO ที่เล่นโวหารเล็กน้อย คุณช่วยให้คำอธิบายสั้น ๆ หรือลิงค์ไปยังที่หนึ่งได้ไหมว่าเหตุใดจึงไม่เป็นที่ต้องการ
mhawke

6
for obj in iterable: d.setdefault(key_for_obj(obj), []).append(obj)คุณจะใช้มันในการแสดงออกที่คาดว่าจะมีมูลค่าอยู่เช่นในวงการจัดกลุ่มต่อไปนี้: ไม่มีอะไรแปลก ๆ เกี่ยวกับการคืนค่านั่นคือจุดรวมของวิธีการนี้
Martijn Pieters

2
นอกจากนี้ยังปิดบังสิ่งที่คุณกำลังพยายามทำซึ่งก็คือการตั้งค่าคีย์เฉพาะในกรณีที่ไม่มีอยู่แล้ว ค่าความสามารถในการอ่านเพียงใช้การif key not in d:ทดสอบ
Martijn Pieters

7
@MartijnPieters: ขอบคุณสำหรับคำอธิบาย เมื่ออ่าน docstring setdefault()ดูเหมือนจะเข้ากับสิ่งที่คุณพูด การใช้defaultdict(list)โค้ดจะส่งผลให้โค้ดอ่านง่ายกว่าตัวอย่างของคุณ ... ดังนั้นอาจจะไม่มีประโยชน์อะไรนอกจากจะต้องทำงานกับพจนานุกรมมาตรฐาน โดยรวมif key not in dชัดเจนกว่า
mhawke

25

ตั้งแต่Python 3.9คุณสามารถใช้ตัวดำเนิน |การผสานเพื่อรวมพจนานุกรมสองพจนานุกรมได้ คำสั่งทางด้านขวามีความสำคัญเหนือกว่า:

new_dict = old_dict | { key: val }

ตัวอย่างเช่น:

new_dict = { 'a': 1, 'b': 2 } | { 'b': 42 }

print(new_dict} # {'a': 1, 'b': 42}

หมายเหตุ: สิ่งนี้จะสร้างพจนานุกรมใหม่พร้อมค่าที่อัปเดต


6
แม้ว่าสิ่งนี้จะน่าสนใจ แต่ฉันไม่คิดว่าจะอ่านได้มากกว่าคำตอบที่ยอมรับ
Justin Furuness

2
สำหรับผมมันเป็นเรื่องดีที่เพียงเพื่อให้รู้ว่ามีวิธีที่จะผสานสอง dicts ตอนนี้
ออสติน

มีความท้าทายกับการรวมนี้ หากคีย์เหมือนกันการผสานจะไม่สนใจค่า ถ้านั่นคือเจตนาก็ใช้ได้
Joe Ferndz

1
@JoeFerndz คำสั่งด้านขวามีความสำคัญเหนือกว่าและจะไม่เพิกเฉยต่อค่า ฉันอัปเดตตัวอย่างเพื่อให้ชัดเจนยิ่งขึ้น
Rotareti

6

ด้วยวิธีต่อไปนี้คุณสามารถแทรกค่าได้หลายค่าและยังมีค่าเริ่มต้น แต่คุณกำลังสร้างพจนานุกรมใหม่

d = {**{ key: value }, **default_values}

ฉันได้ทดสอบด้วยคำตอบที่ได้รับการโหวตมากที่สุดและโดยเฉลี่ยแล้วจะเร็วกว่าดังที่เห็นได้จากตัวอย่างต่อไปนี้

การทดสอบความเร็วเปรียบเทียบวิธีการตามลูปกับการทำความเข้าใจตามคำบอกด้วยตัวดำเนินการแกะกล่อง การทดสอบความเร็วเปรียบเทียบวิธีการตามลูปกับความเข้าใจตามคำบอกด้วยวิธีการแกะกล่อง

หากไม่มีการทำสำเนา ( d = default_vals.copy()) ในกรณีแรกคำตอบที่ได้รับการโหวตมากที่สุดจะเร็วขึ้นเมื่อเราไปถึงคำสั่งที่มีขนาด10**5และมากกว่า รอยเท้าหน่วยความจำของทั้งสองวิธีเหมือนกัน


1
แล้วทำไมถึงแนะนำตั้งแต่แรก
โจนาธาน

1
โซลูชันนี้ดูเหมาะกับฉัน นอกจากนี้ยังอนุญาตให้อัปเดตหลายค่าในการประกาศครั้งเดียว
Rotareti

ขอบคุณมาก. แต่น่าจะเป็น{**default_values, **{ key: value }}
osexp2003

0

ตามคำตอบข้างต้นsetdefault ()วิธีการทำงานสำหรับฉัน

old_attr_name = mydict.setdefault(key, attr_name)
if attr_name != old_attr_name:
    raise RuntimeError(f"Key '{key}' duplication: "
                       f"'{old_attr_name}' and '{attr_name}'.")

แม้ว่าวิธีนี้จะไม่ใช่แบบทั่วไป เหมาะกับฉันในบางกรณีนี้ วิธีแก้ปัญหาที่แน่นอนคือการตรวจสอบkeyครั้งแรก (ตามที่ได้รับคำแนะนำไปแล้ว) แต่เมื่อsetdefault()เราหลีกเลี่ยงการค้นหาเพิ่มเติมในพจนานุกรมนั่นคือแม้ว่าจะมีขนาดเล็ก แต่ก็ยังได้รับประสิทธิภาพ

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