การวนซ้ำพจนานุกรมที่ใช้ลูป 'for'


3138

ฉันสับสนเล็กน้อยตามรหัสต่อไปนี้:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print key, 'corresponds to', d[key]

สิ่งที่ฉันไม่เข้าใจคือkeyส่วนที่ Python รู้ได้อย่างไรว่ามันต้องการเพียงเพื่ออ่านกุญแจจากพจนานุกรม? คือkeyคำที่พิเศษในงูใหญ่? หรือมันเป็นเพียงตัวแปร?


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

คำตอบ:


5392

key เป็นเพียงชื่อตัวแปร

for key in d:

จะวนซ้ำปุ่มต่างๆในพจนานุกรมแทนที่จะเป็นปุ่มและค่า ในการวนซ้ำทั้งคีย์และค่าคุณสามารถใช้สิ่งต่อไปนี้:

สำหรับ Python 3.x:

for key, value in d.items():

สำหรับ Python 2.x:

for key, value in d.iteritems():

ในการทดสอบสำหรับตัวคุณเองเปลี่ยนคำไปkeypoop

ใน Python 3.x iteritems()ถูกแทนที่ด้วยง่ายๆitems()ซึ่งจะคืนค่ามุมมองแบบ set-like ที่สนับสนุนโดย dict เช่นiteritems()แต่ดีกว่า นี้ยังมีอยู่ใน 2.7 viewitems()เป็น

การดำเนินการitems()จะทำงานได้ทั้ง 2 และ 3 แต่ใน 2 จะส่งคืนรายการ(key, value)คู่พจนานุกรมซึ่งจะไม่สะท้อนการเปลี่ยนแปลงของ dict ที่เกิดขึ้นหลังจากการitems()โทร หากคุณต้องการพฤติกรรม 2.x ใน 3.x คุณสามารถโทรlist(d.items())ได้


158
การเพิ่มเหตุผลที่มองข้ามไม่ให้เข้าถึงค่าเช่นนี้: d [คีย์] ใน for for loop ทำให้คีย์ถูกแฮชอีกครั้ง (เพื่อรับค่า) เมื่อพจนานุกรมมีขนาดใหญ่แฮชพิเศษนี้จะเพิ่มเข้ากับเวลาโดยรวม เรื่องนี้มีการกล่าวถึงในการพูดคุยทางเทคนิคของ Raymond Hettinger youtube.com/watch?v=anrOzOapJ2E
quiet_penguin

27
อาจเหมาะสมที่จะกล่าวถึงรายการที่จะซ้ำในลำดับที่คาดเดาไม่ได้และsortedจำเป็นต้องทำให้เสถียร
yugr

5
@HarisankarKrishnaSwamy เป็นทางเลือกอะไร?
JoeyC

3
@yugr ทำไมคุณถึงพูดอย่างนั้น? เอกสารดังกล่าวระบุว่าKeys and values are iterated over in insertion order. [ docs.python.org/3/library/…
Geza Turi

4
@yugr จาก Python 3.7 พจนานุกรมมีคำสั่งแทรกและนี่คือคุณสมบัติภาษา ดูstackoverflow.com/a/39980744/9428564
Aimery

433

ไม่ใช่กุญแจนั่นเป็นคำพิเศษ แต่พจนานุกรมนั้นใช้โปรโตคอลตัววนซ้ำ คุณสามารถทำได้ในชั้นเรียนของคุณเช่นดูคำถามนี้สำหรับวิธีสร้างตัววนซ้ำของคลาส

ในกรณีของพจนานุกรมจะมีการใช้งานที่ระดับ C โดยมีรายละเอียดมีอยู่ในPEP 234 โดยเฉพาะอย่างยิ่งส่วนที่มีชื่อว่า "Dictionary Iterators":

  • พจนานุกรมใช้สล็อต tp_iter ที่ส่งคืนตัววนซ้ำที่มีประสิทธิภาพซึ่งวนซ้ำไปตามคีย์ของพจนานุกรม [... ] ซึ่งหมายความว่าเราสามารถเขียน

    for k in dict: ...

    ซึ่งเทียบเท่ากับ แต่เร็วกว่ามาก

    for k in dict.keys(): ...

    ตราบใดที่ข้อ จำกัด ในการแก้ไขพจนานุกรม (โดยการวนซ้ำหรือโดยเธรดอื่น) จะไม่ถูกละเมิด

  • เพิ่มวิธีการในพจนานุกรมที่คืนค่าตัววนซ้ำประเภทต่าง ๆ อย่างชัดเจน:

    for key in dict.iterkeys(): ...
    
    for value in dict.itervalues(): ...
    
    for key, value in dict.iteritems(): ...

    ซึ่งหมายความว่ามีการจดชวเลขfor x in dictfor x in dict.iterkeys()

ในหลาม 3 dict.iterkeys(), dict.itervalues()และdict.iteritems()ได้รับการสนับสนุนอีกต่อไป ใช้dict.keys(), dict.values()และdict.items()แทน


207

iterating กว่าdictiterates ผ่านปุ่มในลำดับใดที่คุณสามารถดูที่นี่:

แก้ไข: (นี่ไม่ใช่กรณีในPython3.6อีกต่อไปแต่โปรดทราบว่ายังไม่รับประกันพฤติกรรม)

>>> d = {'x': 1, 'y': 2, 'z': 3} 
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']

สำหรับตัวอย่างของคุณควรใช้dict.items():

>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]

สิ่งนี้จะให้รายการของสิ่งอันดับ เมื่อคุณห่วงมากกว่าพวกเขาเช่นนี้แต่ละ tuple เป็นจำนวนมาก ๆ เข้ามาkและvโดยอัตโนมัติ:

for k,v in d.items():
    print(k, 'corresponds to', v)

การใช้kและvเป็นชื่อตัวแปรเมื่อวนลูปมากกว่า a dictเป็นเรื่องปกติถ้าเนื้อความของลูปเป็นเพียงไม่กี่บรรทัด สำหรับลูปที่ซับซ้อนกว่าอาจเป็นความคิดที่ดีที่จะใช้ชื่อที่มีความหมายมากกว่านี้:

for letter, number in d.items():
    print(letter, 'corresponds to', number)

เป็นความคิดที่ดีที่จะใช้นิสัยในการใช้สตริงรูปแบบ:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))

11
จากบันทึกประจำรุ่น Python 3.7: "ลักษณะการเก็บรักษาแบบคำสั่งแทรกของวัตถุ dict ปัจจุบันเป็นส่วนหนึ่งของข้อมูลจำเพาะภาษา Python อย่างเป็นทางการ"
Gregory Arenius

86

key เป็นเพียงตัวแปร

สำหรับPython2.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for my_var in d:
    print my_var, 'corresponds to', d[my_var]

... หรือดีกว่า,

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.iteritems():
    print the_key, 'corresponds to', the_value

สำหรับPython3.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.items():
    print(the_key, 'corresponds to', the_value)

63

เมื่อคุณทำซ้ำผ่านพจนานุกรมโดยใช้for .. in ..-syntax มันจะทำซ้ำมากกว่าคีย์ (ค่าจะสามารถเข้าถึงได้โดยใช้dictionary[key])

ย้ำกว่าคู่คีย์มูลค่าในหลาม 2 การใช้งานfor k,v in s.iteritems()และในหลาม for k,v in s.items()3


38
โปรดทราบว่าสำหรับ Python 3 มันitems()แทนiteritems()
Andreas Fester

32

นี่เป็นสำนวนที่ใช้กันทั่วไปมาก inเป็นผู้ประกอบการ สำหรับเวลาที่จะใช้for key in dictและเมื่อมันจะต้องfor key in dict.keys()ดูบทความสำนวนหลามเดวิด Goodger ของ (คัดลอกเก็บไว้)


เมื่อผมอ่านส่วนเหล่านี้เกี่ยวกับการinเป็นส่วนหนึ่งประกอบการที่คุณตรวจสอบสำหรับการดำรงอยู่ อาจลบin is an operatorข้อมูลนี้ได้ดีกว่า
Wolf

19

การวนซ้ำพจนานุกรมที่ใช้ลูป 'for'

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    ...

Python รู้ได้อย่างไรว่ามันต้องการเพียงเพื่ออ่านกุญแจจากพจนานุกรม? คีย์เป็นคำพิเศษใน Python หรือไม่ หรือมันเป็นเพียงตัวแปร?

มันไม่ใช่แค่forลูป คำสำคัญที่นี่คือ "วนซ้ำ"

พจนานุกรมคือการแม็พของคีย์กับค่า:

d = {'x': 1, 'y': 2, 'z': 3} 

เมื่อใดก็ตามที่เราทำซ้ำมันเราจะทำซ้ำมากกว่ากุญแจ ชื่อตัวแปรkeyมีจุดประสงค์เพื่อให้สื่อความหมายเท่านั้น - และเหมาะสำหรับวัตถุประสงค์

สิ่งนี้เกิดขึ้นในรายการความเข้าใจ:

>>> [k for k in d]
['x', 'y', 'z']

มันเกิดขึ้นเมื่อเราส่งพจนานุกรมไปยังรายการ (หรือวัตถุประเภทคอลเลกชันอื่น ๆ ):

>>> list(d)
['x', 'y', 'z']

วิธีการทำซ้ำของ Python ในบริบทที่ต้องการมันเรียก__iter__เมธอดของวัตถุ (ในกรณีนี้คือพจนานุกรม) ซึ่งส่งคืนตัววนซ้ำ (ในกรณีนี้คือวัตถุ keyiterator):

>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>

เราไม่ควรใช้วิธีพิเศษเหล่านี้แทนใช้ฟังก์ชัน builtin ตามลำดับแทนiter:

>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>

Iterators มี__next__เมธอด - แต่เราเรียกมันด้วยฟังก์ชัน builtin next:

>>> next(key_iterator)
'x'
>>> next(key_iterator)
'y'
>>> next(key_iterator)
'z'
>>> next(key_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

เมื่อ iterator StopIterationหมดมันก็เกิด นี่คือวิธีที่ Python รู้วิธีออกจากforลูปหรือความเข้าใจในรายการหรือนิพจน์ตัวสร้างหรือบริบทการวนซ้ำอื่น ๆ เมื่อตัววนซ้ำยกขึ้นStopIterationมันก็จะเพิ่มมันเสมอ - หากคุณต้องการวนซ้ำอีกครั้งคุณจะต้องเพิ่มใหม่

>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']

กลับไปที่ dicts

เราได้เห็นว่าวนซ้ำในบริบทหลายอย่าง สิ่งที่เราได้เห็นคือเมื่อใดก็ตามที่เราย้ำกว่า Dict เราจะได้รับกุญแจ กลับไปที่ตัวอย่างดั้งเดิม:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:

หากเราเปลี่ยนชื่อตัวแปรเรายังคงได้รับกุญแจ มาลองดูกัน:

>>> for each_key in d:
...     print(each_key, '=>', d[each_key])
... 
x => 1
y => 2
z => 3

หากเราต้องการวนซ้ำค่าเราจำเป็นต้องใช้.valuesวิธีการกำหนดหรือสำหรับทั้งสองร่วมกัน.items:

>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]

ในตัวอย่างที่ให้มามันจะมีประสิทธิภาพมากกว่าในการวนซ้ำไอเท็มแบบนี้:

for a_key, corresponding_value in d.items():
    print(a_key, corresponding_value)

แต่สำหรับจุดประสงค์ทางวิชาการตัวอย่างของคำถามก็ใช้ได้


17

ฉันมีกรณีการใช้งานที่ฉันต้องวนซ้ำตามคำสั่งเพื่อรับคีย์คู่ค่าและดัชนีที่ระบุว่าฉันอยู่ที่ไหน นี่คือวิธีที่ฉันทำ:

d = {'x': 1, 'y': 2, 'z': 3} 
for i, (key, value) in enumerate(d.items()):
   print(i, key, value)

โปรดทราบว่าวงเล็บที่อยู่รอบ ๆ คีย์นั้นมีความสำคัญโดยที่ไม่ต้องใส่วงเล็บคุณจะได้รับ ValueError "ค่าไม่เพียงพอสำหรับการแกะ"


1
สิ่งนี้เกี่ยวข้องกับคำถามอย่างไร
jorijnsmit

8

คุณสามารถตรวจสอบการใช้งาน CPython ได้dicttypeที่ GitHub นี่คือลายเซ็นของวิธีการที่ใช้ dict iterator:

_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
             PyObject **pvalue, Py_hash_t *phash)

CPython dictobject.c


4

หากต้องการวนซ้ำปุ่มก็ช้าลง แต่ควรใช้งานmy_dict.keys()ได้ดีกว่า หากคุณพยายามทำสิ่งนี้:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

มันจะสร้างข้อผิดพลาด runtime เพราะคุณกำลังเปลี่ยนคีย์ในขณะที่โปรแกรมกำลังทำงาน หากคุณตั้งเวลาในการลดเวลาอย่างแน่นอนให้ใช้for key in my_dictวิธีนี้ แต่คุณได้รับคำเตือนแล้ว)


2

สิ่งนี้จะพิมพ์ผลลัพธ์ในลำดับที่เรียงตามค่าในลำดับจากน้อยไปหามาก

d = {'x': 3, 'y': 1, 'z': 2}
def by_value(item):
    return item[1]

for key, value in sorted(d.items(), key=by_value):
    print(key, '->', value)

เอาท์พุท:

ป้อนคำอธิบายรูปภาพที่นี่

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