วัตถุมุมมองพจนานุกรมคืออะไร?


158

ใน python 2.7 เรามีวิธีการดูพจนานุกรมที่มีอยู่

ตอนนี้ฉันรู้ข้อดีและข้อเสียของสิ่งต่อไปนี้:

  • dict.items()(และvalues, keys): ส่งคืนรายการดังนั้นคุณจึงสามารถจัดเก็บผลลัพธ์ได้จริงและ
  • dict.iteritems() (และชอบ): ส่งคืนตัวสร้างดังนั้นคุณสามารถวนซ้ำแต่ละค่าที่สร้างทีละตัว

อะไรคือสิ่งที่dict.viewitems()และชอบ ประโยชน์ของพวกเขาคืออะไร? มันทำงานยังไง? อะไรคือมุมมองหลังจากทั้งหมด?

ฉันอ่านว่ามุมมองสะท้อนการเปลี่ยนแปลงจากพจนานุกรมเสมอ แต่มันทำงานอย่างไรจากมุมมองที่สมบูรณ์และหน่วยความจำ? ข้อดีและข้อเสียคืออะไร

คำตอบ:


157

มุมมองพจนานุกรมเป็นสิ่งสำคัญที่ชื่อของพวกเขาพูดว่า: มุมมองเป็นเหมือนหน้าต่างบนปุ่มและค่า (หรือรายการ) ของพจนานุกรม นี่คือข้อความที่ตัดตอนมาจากเอกสารอย่างเป็นทางการสำหรับ Python 3:

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()

>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> keys  # No eggs anymore!
dict_keys(['sausage', 'bacon', 'spam'])

>>> values  # No eggs value (2) anymore!
dict_values([1, 1, 500])

(การใช้ Python 2 ที่เทียบเท่าdishes.viewkeys()และdishes.viewvalues().)

ตัวอย่างนี้แสดงลักษณะของมุมมองแบบไดนามิก : มุมมองคีย์ไม่ใช่สำเนาของคีย์ ณ เวลาที่กำหนด แต่เป็นหน้าต่างแบบง่ายที่แสดงคีย์ให้คุณ หากมีการเปลี่ยนแปลงสิ่งที่คุณเห็นผ่านหน้าต่างก็จะเปลี่ยนแปลงเช่นกัน คุณลักษณะนี้มีประโยชน์ในบางสถานการณ์ (ตัวอย่างเช่นเราสามารถทำงานกับมุมมองบนคีย์ในหลายส่วนของโปรแกรมแทนที่จะคำนวณรายการคีย์ปัจจุบันในแต่ละครั้งที่จำเป็น) - หมายเหตุว่าหากมีการแก้ไขคีย์พจนานุกรม ในขณะที่การทำซ้ำมากกว่ามุมมองวิธีการ iterator ควรประพฤติไม่ได้กำหนดไว้อย่างดีซึ่งสามารถนำไปสู่ข้อผิดพลาด

ข้อดีอย่างหนึ่งก็คือการดูพูดว่าคีย์ใช้หน่วยความจำเพียงเล็กน้อยและคงที่และต้องใช้เวลาในการประมวลผลเล็กน้อยเนื่องจากไม่มีการสร้างรายการของคีย์ (Python 2) มักจะสร้างรายการใหม่โดยไม่จำเป็นตามที่ยกมาโดย Rajendran T ซึ่งใช้หน่วยความจำและเวลาในปริมาณตามสัดส่วนกับความยาวของรายการ) หากต้องการดูหน้าต่างต่อไปหากคุณต้องการเห็นภูมิทัศน์ด้านหลังกำแพงคุณเพียงแค่ทำการเปิดใน (คุณสร้างหน้าต่าง) การคัดลอกคีย์ลงในรายการจะสอดคล้องกับการวาดสำเนาภูมิทัศน์บนผนังของคุณแทนการคัดลอกต้องใช้เวลาเนื้อที่และไม่อัปเดตตัวเอง

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


6
+1 ตกลงนั่นแตกต่างจากการเข้าถึงรายการคีย์ภายในโดยตรงอย่างไร เร็วขึ้นช้าลงไหม? หน่วยความจำมีประสิทธิภาพมากขึ้น? ถูก จำกัด ? หากคุณสามารถอ่านและแก้ไขมันรู้สึกเหมือนกับว่ามีการอ้างอิงไปยังรายการนี้
E-satis

3
ขอบคุณ สิ่งสำคัญคือมุมมองคือการเข้าถึง "รายการกุญแจภายใน" (โปรดทราบว่า "รายการกุญแจ" นี้ไม่ใช่รายการ Python แต่เป็นมุมมองที่แม่นยำ) จำนวนการดูมีประสิทธิภาพมากกว่าหน่วยความจำมากกว่ารายการของปุ่ม (หรือค่าหรือรายการ) ของ Python 2 เนื่องจากไม่ได้คัดลอกอะไรเลย พวกเขาชอบ "การอ้างอิงถึงรายการคีย์" (โปรดทราบด้วยว่า "การอ้างอิงถึงรายการ" จริงๆแล้วเรียกง่ายๆว่ารายการใน Python เนื่องจากรายการเป็นวัตถุที่ไม่แน่นอน) นอกจากนี้โปรดทราบว่าคุณไม่สามารถแก้ไขมุมมองโดยตรง: แทนคุณยังคงแก้ไขพจนานุกรมและมุมมองจะแสดงการเปลี่ยนแปลงของคุณทันที
Eric O Lebigot

3
ตกลงฉันยังไม่ชัดเจนเกี่ยวกับการนำไปใช้ แต่ยังเป็นคำตอบที่ดีที่สุด
E-satis

2
ขอบคุณ แน่นอนคำตอบนี้ส่วนใหญ่เกี่ยวกับความหมายของมุมมอง ฉันไม่มีข้อมูลเกี่ยวกับการใช้งานใน CPython แต่ฉันเดาว่ามุมมองนั้นเป็นตัวชี้ไปยังโครงสร้างที่ถูกต้อง (คีย์และ / หรือค่า) และโครงสร้างเป็นส่วนหนึ่งของวัตถุพจนานุกรม
Eric O Lebigot

5
ฉันคิดว่ามันคุ้มค่าที่จะชี้ให้เห็นว่าโค้ดตัวอย่างในโพสต์นี้มาจาก python3 และไม่ใช่สิ่งที่ฉันได้รับใน python2.7
snth

21

ดังที่คุณกล่าวถึงdict.items()จะส่งคืนสำเนาของรายการของคู่ (คีย์, ค่า) ซึ่งสิ้นเปลืองและdict.iteritems()ส่งคืนตัววนซ้ำของคู่ (คีย์, ค่า) ของพจนานุกรม

ตอนนี้ใช้ตัวอย่างต่อไปนี้เพื่อดูความแตกต่างระหว่างตัวคั่นของ dict และมุมมองของ dict

>>> d = {"x":5, "y":3}
>>> iter = d.iteritems()
>>> del d["x"]
>>> for i in iter: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

ในขณะที่มุมมองเพียงแสดงให้คุณเห็นสิ่งที่อยู่ใน Dict มันไม่สนใจว่ามันจะเปลี่ยนไปหรือไม่:

>>> d = {"x":5, "y":3}
>>> v = d.viewitems()
>>> v
dict_items([('y', 3), ('x', 5)])
>>> del d["x"]
>>> v
dict_items([('y', 3)])

มุมมองเป็นเพียงสิ่งที่พจนานุกรมดูเหมือนว่าตอนนี้ หลังจากลบรายการ.items()จะล้าสมัยและ.iteritems()มีข้อผิดพลาดเกิดขึ้น


ตัวอย่างที่ดีขอบคุณ แม้ว่าควรเป็น v = d.items () ไม่ใช่ v - d.viewitems ()
rix

1
คำถามเกี่ยวกับ Python 2.7 ดังนั้นviewitems()ถูกต้องจริง ( items()ให้มุมมองใน Python 3อย่างถูกต้อง)
Eric O Lebigot

อย่างไรก็ตามไม่สามารถใช้มุมมองเพื่อวนซ้ำพจนานุกรมขณะแก้ไขได้
Ioannis Filippidis

18

เพียงแค่อ่านเอกสารฉันได้รับความประทับใจนี้:

  1. มุมมองคือ "หลอกเหมือนชุด" ซึ่งพวกเขาไม่สนับสนุนการจัดทำดัชนีดังนั้นสิ่งที่คุณสามารถทำได้คือทดสอบการเป็นสมาชิกและวนซ้ำไปเรื่อย ๆ (เพราะคีย์มีการแฮชและไม่ซ้ำกันมุมมองคีย์และรายการมีมากกว่า " set-like "ที่พวกเขาไม่มีรายการซ้ำกัน)
  2. คุณสามารถจัดเก็บและใช้งานได้หลายครั้งเช่นเวอร์ชันรายการ
  3. เนื่องจากมันสะท้อนถึงพจนานุกรมพื้นฐานการเปลี่ยนแปลงใด ๆ ในพจนานุกรมจะเปลี่ยนมุมมองและจะเปลี่ยนลำดับการวนซ้ำอย่างแน่นอน ซึ่งแตกต่างจากเวอร์ชันของรายการพวกเขาไม่ "เสถียร"
  4. เพราะมันสะท้อนถึงพจนานุกรมพื้นฐานพวกมันเกือบจะเป็นวัตถุพร็อกซี่เล็ก ๆ การคัดลอกคีย์ / ค่า / ไอเท็มจะต้องให้พวกเขาดูพจนานุกรมต้นฉบับอย่างใดและคัดลอกหลายครั้งเมื่อมีการเปลี่ยนแปลงเกิดขึ้นซึ่งจะเป็นการใช้งานที่ไร้สาระ ดังนั้นฉันคาดหวังค่าใช้จ่ายหน่วยความจำน้อยมาก แต่การเข้าถึงช้ากว่าพจนานุกรมโดยตรงเล็กน้อย

ดังนั้นฉันจึงเดาว่าการใช้คีย์คือถ้าคุณเก็บพจนานุกรมไว้และวนซ้ำ ๆ ซ้ำ ๆ คีย์ / ไอเท็ม / ค่าที่มีการปรับเปลี่ยนในระหว่างนั้น คุณก็สามารถใช้มุมมองแทนที่จะหันเข้าfor k, v in mydict.iteritems(): for k, v in myview:แต่ถ้าคุณแค่วนซ้ำพจนานุกรมหนึ่งครั้งฉันคิดว่ารุ่นตัวเลือกยังดีกว่า


2
+1 สำหรับการวิเคราะห์ข้อดีข้อเสียจากข้อมูลที่เราได้รับ
E-satis

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

@Alfe คุณพูดถูกที่เป็นปัญหากับการวนซ้ำของพจนานุกรมและมุมมองไม่ได้ช่วยอะไรเลย สมมติว่าคุณต้องส่งค่าพจนานุกรมไปยังฟังก์ชัน คุณสามารถใช้.values()แต่ที่เกี่ยวข้องกับการทำสำเนาทั้งหมดเป็นรายการซึ่งอาจมีราคาแพง มี.itervalues()แต่คุณไม่สามารถกินมากกว่าหนึ่งครั้งดังนั้นมันจะไม่ทำงานกับทุกฟังก์ชั่น จำนวนการดูไม่ต้องการสำเนาที่มีราคาแพง แต่ยังคงมีประโยชน์มากกว่าในฐานะค่าสแตนด์อะโลนกว่าตัววนซ้ำ แต่พวกเขาก็ยังไม่ได้ตั้งใจที่จะช่วยในการทำซ้ำและแก้ไขในเวลาเดียวกัน (ที่นั่นคุณต้องการสำเนา)
Ben

17

มุมมองวิธีการกลับรายการ (ไม่สำเนาของรายการเมื่อเทียบกับ.keys(), .items()และ.values()) ดังนั้นจึงมีน้ำหนักเบามากขึ้น แต่สะท้อนให้เห็นถึงเนื้อหาปัจจุบันของพจนานุกรม

จากPython 3.0 - เมธอด dict ส่งคืนมุมมอง - เพราะอะไร

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

หากคุณต้องการทำซ้ำคีย์มากกว่าการสร้างรายการใหม่ก็ไม่จำเป็น และถ้าคุณต้องการมันเป็นรายการแยกต่างหาก (เป็นสำเนา) คุณสามารถสร้างรายการนั้นจากมุมมองได้อย่างง่ายดาย


6
เมธอด view ส่งคืนอ็อบเจ็กต์มุมมองซึ่งไม่สอดคล้องกับอินเตอร์เฟสรายการ
Matthew Trevor

5

มุมมองช่วยให้คุณเข้าถึงโครงสร้างข้อมูลที่อยู่ภายใต้โดยไม่ต้องคัดลอก นอกเหนือจากการเป็นแบบไดนามิกซึ่งต่างจากการสร้างรายการหนึ่งในการใช้งานที่มีประโยชน์ที่สุดคือinการทดสอบ สมมติว่าคุณต้องการตรวจสอบว่าค่าอยู่ใน dict หรือไม่ (ไม่ว่าจะเป็นคีย์หรือค่า)

ตัวเลือกที่หนึ่งคือการสร้างรายการคีย์ที่ใช้dict.keys()งานได้ แต่เห็นได้ชัดว่าต้องใช้หน่วยความจำมากขึ้น ถ้า dict มีขนาดใหญ่มาก? นั่นจะสิ้นเปลือง

ด้วยviewsคุณสามารถทำซ้ำโครงสร้างข้อมูลจริงโดยไม่ต้องรายการกลาง

ลองใช้ตัวอย่าง ฉันเขียนตามตัวเลขและสตริงแบบสุ่ม 1,000 คีย์และkเป็นกุญแจที่ฉันต้องการค้นหา

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. }

>>> len(large_d)
1000

# this is one option; It creates the keys() list every time, it's here just for the example
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000)
13.748743600954867


# now let's create the list first; only then check for containment
>>> list_keys = large_d.keys()
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000)
8.874809793833492


# this saves us ~5 seconds. Great!
# let's try the views now
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000)
0.08828549011070663

# How about saving another 8.5 seconds?

อย่างที่คุณเห็นviewวัตถุวนซ้ำนั้นช่วยเพิ่มประสิทธิภาพได้อย่างมากลดค่าใช้จ่ายหน่วยความจำในเวลาเดียวกัน คุณควรใช้พวกเขาเมื่อคุณต้องการดำเนินการSetเช่นการดำเนินการ

หมายเหตุ : ฉันกำลังใช้งาน Python 2.7


ใน python> = 3 ฉันเชื่อว่า.keys()ส่งคืนมุมมองตามค่าเริ่มต้น อาจต้องการตรวจสอบอีกครั้งโท
Yolo Voe

1
คุณถูก. Python 3+ ทำการใช้งานมุมมองวัตถุอย่างหนักแทนที่จะเป็นรายการมันมีความจำดีกว่ามาก
Chen A.

1
ผลลัพธ์การจับเวลาเหล่านี้บอกได้ดีมาก แต่การตรวจสอบว่าkเป็นหนึ่งในกุญแจของพจนานุกรมlarge_dที่จะทำด้วยk in large_dหรือไม่ใน Python ซึ่งน่าจะเร็วพอ ๆ กับการใช้มุมมอง (ในคำอื่น ๆk in large_d.keys()ไม่ใช่ Pythonic และควรหลีกเลี่ยง - ตามที่เป็นk in large_d.viewkeys())
Eric O Lebigot

ขอบคุณที่ให้ตัวอย่างที่มั่นคงและมีประโยชน์ k in large_dเป็นจริงอย่างเร็วกว่าk in large_d.viewkeys()เพื่อที่ว่าอาจจะหลีกเลี่ยงได้ k in large_d.viewvalues()แต่นี้ทำให้รู้สึกสำหรับ
naught101
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.