เหตุใด dict.get (คีย์) จึงทำงาน แต่ไม่ใช่ [dict]


17

ฉันกำลังพยายามจัดกลุ่มสตริงไบนารี่ของตัวเลขบางตัวร่วมกันโดยพิจารณาจากจำนวน 1 ในสตริง

สิ่งนี้ใช้ไม่ได้:

s = "0 1 3 7 8 9 11 15"
numbers = map(int, s.split())
binaries = [bin(x)[2:].rjust(4, '0') for x in numbers]

one_groups = dict.fromkeys(range(5), [])
for x in binaries:
    one_groups[x.count('1')] += [x]

พจนานุกรมที่คาดหวังone_groupsจะต้องมี

{0: ['0000'], 
 1: ['0001', '1000'], 
 2: ['0011', '1001'], 
 3: ['0111', '1011'], 
 4: ['1111']}

แต่ฉันได้

{0: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 1: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 2: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 3: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 4: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111']}

จนถึงตอนนี้สิ่งเดียวที่ใช้ได้คือถ้าฉันใช้one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]แทนone_groups[x.count('1')] += [x]

แต่ทำไมถึงเป็นเช่นนั้น? หากฉันจำได้อย่างถูกต้องไม่dict[key]ควรส่งคืนค่าของพจนานุกรมนั้นคล้ายกับวิธีการdict.get(key)ทำงานอย่างไร ฉันเคยเห็นกระทู้นี้แล้วทำไม dict.get (กุญแจ) แทนที่จะเป็น dict [key] แต่มันไม่ได้ตอบคำถามของฉันสำหรับกรณีนี้เนื่องจากฉันรู้ว่าโปรแกรมไม่ได้หมายถึงการได้รับKeyError

ฉันเคยลองแล้วone_groups[x.count('1')].append(x)แต่มันก็ไม่ได้ผลเหมือนกัน


8
getส่งคืนNoneหากไม่มีคีย์หรือค่าเริ่มต้นใด ๆ ที่ให้มาในขณะที่ผู้ดำเนินการดัชนี[]แจ้งข้อผิดพลาดหากไม่มีคีย์
adnanmuttaleb

Sidenote bin(x)[2:].rjust(4, '0')สามารถทำให้ง่าย'{:0>4b}'.format(x)ขึ้น
wjandrea

1
BTW จะช่วยทำให้ตัวอย่างเช่นสามารถทำซ้ำได้น้อยที่สุด ในกรณีนี้วิธีที่คุณทำbinariesไม่เกี่ยวข้องกับคำถามดังนั้นคุณสามารถระบุมูลค่าของมันได้
wjandrea

1
นี่ตอบคำถามของคุณหรือไม่? dict.fromkeys ทั้งหมดชี้ไปที่รายการเดียวกัน
Georgy

คำตอบ:


24

ปัญหาคือความไม่แน่นอน:

one_groups = dict.fromkeys(range(5), [])- นี้ผ่านรายการเดียวกันเป็นมูลค่าให้กับคีย์ทั้งหมด ดังนั้นหากคุณเปลี่ยนค่าหนึ่งค่าคุณจะเปลี่ยนค่าทั้งหมด

โดยพื้นฐานแล้วมันเหมือนกับว่า:

tmp = []
one_groups = dict.fromkeys(range(5), tmp)
del tmp

หากคุณต้องการใช้รายการใหม่คุณต้องทำในลูป - ทั้งforลูปอย่างชัดเจนหรือในความเข้าใจ dict:

one_groups = {key: [] for key in range(5)}

สิ่งนี้จะ "ดำเนินการ" [](ซึ่งเท่ากับlist()) สำหรับทุกคีย์ดังนั้นจึงทำให้ค่าที่มีรายการที่แตกต่างกัน


ทำไมถึงgetทำงาน เพราะคุณใช้รายการปัจจุบันอย่างชัดเจน แต่+สร้างรายการผลลัพธ์ใหม่ และมันไม่ได้เรื่องไม่ว่าจะเป็นone_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]หรือone_groups[x.count('1')] = one_groups[x.count('1')] + [x]- +สิ่งที่สำคัญคือว่ามี

ฉันรู้ว่าทุกคนบอกว่าa+=bเป็นเพียงแค่a=a+bแต่การใช้งานอาจแตกต่างกันสำหรับการปรับให้เหมาะสม - ในกรณีของรายการ+=เป็นเพียง.extendเพราะเรารู้ว่าเราต้องการผลลัพธ์ของเราในตัวแปรปัจจุบันดังนั้นการสร้างรายการใหม่จะเสียความทรงจำ


อ่าเข้าใจแล้ว ฉันยังจำได้ว่ามีปัญหาคล้ายกันเมื่อฉันต้องการสร้างรายการ 2D โดยใช้mylist = [[] * 5] * 5และmylist = [[] for x in range(5)] * 5จะแก้ไขได้อย่างไร เพื่อความกระจ่างอย่างรวดเร็วจากที่ฉันเข้าใจว่าสิ่งนี้เกิดขึ้นเพราะตัวแปรที่ชี้ไปยังที่อยู่หน่วยความจำของรายการว่างเปล่านั้น นี่ก็หมายความว่าปัญหาจะไม่เกิดขึ้นถ้าฉันใช้ดั้งเดิมแทน?
SpectraXCD

1
ใช่ถ้าคุณใช้วิธีดั้งเดิมจะเป็นการแก้ปัญหา แต่จะแตก one_groups[x.count('1')] += [x]เพราะคุณไม่สามารถเพิ่มรายการลงในประเภทดั้งเดิมได้ ทางออกที่ดีกว่าคือการใช้ defaultdict แทน
Fakher Mokadem

4
โดยเฉพาะการ+โทร__add__และส่งคืนวัตถุใหม่ในขณะที่+=โทร__iadd__และไม่จำเป็นต้องส่งคืนวัตถุใหม่
njzk2

8

ปัญหากำลังใช้งาน one_groups = dict.fromkeys(range(5), [])

(สิ่งนี้จะผ่านรายการเดียวกับค่าไปยังคีย์ทั้งหมดดังนั้นหากคุณเปลี่ยนค่าหนึ่งค่าคุณจะเปลี่ยนค่าทั้งหมด)


คุณสามารถใช้สิ่งนี้แทน: one_groups = {i:[] for i in range(5)}

(สิ่งนี้จะ "ดำเนินการ" [] (ซึ่งเท่ากับรายการ ()) สำหรับทุกคีย์ดังนั้นจึงทำให้ค่าที่มีรายการที่แตกต่างกัน)


6
คุณพูดถูกแม้ว่าคำอธิบายจะเป็นประโยชน์จริงๆ จริงๆแล้วมันไม่ได้เป็นสิ่งที่ทำให้เกิดความแตกต่างระหว่างสองบรรทัด
Simon Fink

ใช่มันเป็นสิ่งที่ไม่ดีของฉัน ขอโทษ
Hameda169

4

นี่คือความช่วยเหลือในfromkeysวิธีการของ Dict

ช่วยเหลือเกี่ยวกับฟังก์ชั่นในตัวจากปุ่ม:

fromkeys (iterable, value = None, /) วิธีการของอินสแตนซ์ builtins.type สร้างพจนานุกรมใหม่ด้วยคีย์จาก iterable และตั้งค่าเป็นค่า

นั่นบอกว่า fromkeys จะยอมรับค่าและแม้กระทั่งเป็น callable มันจะประเมินค่าก่อนแล้วจึงกำหนดค่านั้นให้กับคีย์ dict ทั้งหมด

รายการนั้นไม่สามารถเปลี่ยนแปลงได้ใน Python ดังนั้นมันจะกำหนดการอ้างอิงรายการว่างเปล่าเดียวกันและการเปลี่ยนแปลงหนึ่งรายการจะส่งผลกระทบต่อพวกเขาทั้งหมด

ใช้ defaultdict แทนดังนั้น:

>>> from collections import defaultdict
>>> one_groups = defaultdict(list)
>>> for x in binaries:
      one_groups[x.count('1')] += [x]
>>> one_groups = dict(one_groups) # to stop default dict behavior

สิ่งนี้จะยอมรับการมอบหมายให้กับคีย์และค่าที่ไม่มีอยู่และจะใช้ค่าเริ่มต้นเป็นรายการที่ว่างเปล่า (ในกรณีนี้)

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