ทำไมไพ ธ อนไม่ตั้งค่ารักษาลำดับการแทรก


12

ฉันรู้สึกประหลาดใจที่ค้นพบเมื่อเร็ว ๆ นี้ว่าในขณะที่ dicts รับประกันเพื่อรักษาลำดับการแทรกใน Python 3.7+ ชุดจะไม่:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

เหตุผลสำหรับความแตกต่างนี้คืออะไร? การปรับปรุงประสิทธิภาพแบบเดียวกันนี้ทำให้ทีม Python เปลี่ยนการใช้ dict ไม่ได้ใช้กับชุดเช่นกันหรือไม่?

ฉันไม่ได้มองหาพอยน์เตอร์สำหรับการนำไปใช้ตามคำสั่งหรือวิธีใช้ dicts เป็นชุดสแตนด์บาย ฉันแค่สงสัยว่าทำไมทีม Python จึงไม่สร้างฉากในตัวในเวลาเดียวกันพวกเขาก็ทำเช่นนั้น


1
สิ่งนี้ตอบคำถามของคุณหรือไม่ Python มีชุดสั่งซื้อหรือไม่
Mihai Chelaru

1
ไม่ฉันเข้าใจว่า Python ไม่มีชุดคำสั่งติดตั้งอยู่ภายในฉันแค่สงสัยว่าทำไมในกรณีนี้เนื่องจาก dicts ได้รับคำสั่งแล้ว
บาร์ตโรบินสัน

4
รูปแบบการใช้งานแตกต่างกันดังนั้นจึงได้รับการปรับให้เหมาะสมกับการใช้งานที่แตกต่างกัน มันเป็นความเข้าใจผิดที่พบโดยทั่วไปว่าชุดนั้นมีค่าเป็นศูนย์ใน CPython ซึ่งไม่ถูกต้องทั้งหมด: การนำไปใช้งานนั้นแตกต่างกัน หากคำถามของคุณไม่ปิดฉันสามารถโพสต์คำตอบโดยละเอียด
Wim

1
"รูปแบบการใช้งานแตกต่างกันดังนั้นจึงได้รับการปรับให้เหมาะสมกับการใช้งานที่แตกต่างกัน" ฉันคิดว่าคำตอบที่ดีสำหรับคำถามนี้ คำถามคือเกี่ยวกับสิ่งที่ทำให้ทั้งสองแนวทางที่ดีที่สุดสำหรับกรณีการใช้งานที่สอดคล้องกัน
Karl Knechtel

โปรดทราบว่า PyPy ใช้คำสั่งเดียวกันสำหรับทั้งคู่dictและsetตั้งแต่ 2.7
MisterMiyagi

คำตอบ:


10

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

ในขณะที่โครงสร้างข้อมูลทั้งสองนั้นใช้แฮชเป็นความเข้าใจผิดทั่วไปที่ชุดข้อมูลจะถูกนำไปใช้เป็น dicts ด้วยค่า Null แม้กระทั่งก่อนที่การใช้งาน dict ขนาดกะทัดรัดใน CPython 3.6 การใช้งานชุดและ dict นั้นแตกต่างกันอย่างมากโดยใช้รหัสซ้ำ ตัวอย่างเช่น dicts ใช้การตรวจสอบแบบสุ่ม แต่ชุดใช้การรวมกันของการตรวจสอบเชิงเส้นและการเปิดที่อยู่เพื่อปรับปรุงท้องถิ่นแคช โพรบเชิงเส้นเริ่มต้น ( 9 ขั้นตอนเริ่มต้นใน CPython) จะตรวจสอบชุดของคู่คีย์ / แฮชที่อยู่ติดกันซึ่งเป็นการปรับปรุงประสิทธิภาพโดยการลดค่าใช้จ่ายในการจัดการการชนกันของแฮช - การเข้าถึงหน่วยความจำต่อเนื่อง

มันเป็นไปได้ในทางทฤษฎีที่จะเปลี่ยนการดำเนินงานของ CPython ให้คล้ายกับ dict ขนาดกะทัดรัด แต่ในทางปฏิบัติมีข้อเสียและผู้พัฒนาหลักที่โดดเด่นไม่เห็นด้วยกับการเปลี่ยนแปลงดังกล่าว

ชุดยังคงไม่มีการเรียงลำดับ (เพราะอะไรรูปแบบการใช้งานนั้นแตกต่างกันไป

- Guido van Rossum

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

- Raymond Hettinger

การอภิปรายโดยละเอียดว่าจะทำการบีบอัดชุดสำหรับ 3.7 และคำตอบเกี่ยวกับสาเหตุที่เลือกได้หรือไม่สามารถดูได้ในรายการส่งเมล์ของ python-dev

โดยสรุปประเด็นหลักคือรูปแบบการใช้งานที่แตกต่างกัน (การใส่ dicts การสั่งซื้อเช่น ** kwargs มีประโยชน์น้อยกว่าสำหรับชุด) การประหยัดพื้นที่สำหรับชุดการบีบอัดมีความสำคัญน้อยกว่า (เนื่องจากมีเฉพาะคีย์และอาร์เรย์แฮช densify ซึ่งตรงข้ามกับคีย์แฮชและค่า) และการเพิ่มประสิทธิภาพการตรวจสอบเชิงเส้นที่กล่าวมาข้างต้นในชุดนั้นไม่เข้ากันกับการใช้งานแบบกะทัดรัด

ฉันจะทำซ้ำโพสต์ของเรย์มอนด์ด้านล่างซึ่งครอบคลุมจุดที่สำคัญที่สุด

ในวันที่ 14 ก.ย. 2559 เวลา 15:50 น. Eric Snow เขียนว่า:

จากนั้นฉันก็จะทำแบบเดียวกัน

เรย์มอนด์ไม่เห็นด้วยกับการเปลี่ยนแปลงที่คล้ายกัน

ถูกตัอง. ต่อไปนี้เป็นความคิดบางประการเกี่ยวกับเรื่องนี้ก่อนที่ผู้คนจะเริ่มคลั่งไคล้

  • สำหรับ dict ขนาดกะทัดรัดการประหยัดพื้นที่เป็นชัยชนะสุทธิโดยมีพื้นที่เพิ่มเติมที่ใช้โดยดัชนีและการหาตำแหน่งโดยรวมสำหรับอาร์เรย์คีย์ / ค่า / แฮชมากกว่าที่จะชดเชยด้วยความหนาแน่นที่เพิ่มขึ้นของคีย์ / ค่า / แฮชอาร์เรย์ อย่างไรก็ตามสำหรับชุดสุทธิน้อยกว่าที่น่าพอใจมากเพราะเรายังต้องการดัชนีและตำแหน่งโดยรวม แต่สามารถชดเชยต้นทุนพื้นที่ได้โดยลดความหนาแน่นของอาร์เรย์สองในสามเท่านั้น กล่าวอีกนัยหนึ่งการกระชับจะเหมาะสมกว่าเมื่อคุณเสียพื้นที่สำหรับคีย์ค่าและแฮช หากคุณสูญเสียหนึ่งในสามเหล่านั้นมันจะหยุดการดึงดูด

  • รูปแบบการใช้สำหรับชุดนั้นแตกต่างจาก dicts อดีตมีการค้นหาที่โดนหรือพลาดมากขึ้น หลังมีแนวโน้มที่จะค้นหาคีย์หายไปน้อยลง นอกจากนี้การเพิ่มประสิทธิภาพบางอย่างสำหรับการดำเนินการแบบ set-to-set ทำให้ยากต่อการรักษาลำดับการสั่งซื้อโดยไม่ส่งผลกระทบต่อประสิทธิภาพ

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

  • สำหรับตอนนี้การสั่งซื้อผลข้างเคียงกับพจนานุกรมนั้นไม่ได้รับการรับประกันดังนั้นจึงเป็นการเริ่มต้นที่จะยืนยันว่าชุดคำสั่งจะถูกสั่งเช่นกัน เอกสารเชื่อมโยงไปยังสูตรสำหรับสร้าง OrderedSet แล้ว ( https://code.activestate.com/recipes/576694/ ) แล้ว แต่ดูเหมือนว่าการดูดซึมจะเกือบเป็นศูนย์ ตอนนี้ Eric Snow ให้ OrderedDict ที่รวดเร็วกับเรามันง่ายกว่าที่เคยสร้าง OrderedSet จาก MutableSet และ OrderedDict แต่อีกครั้งฉันไม่ได้สังเกตความสนใจจริง ๆ เพราะการวิเคราะห์ข้อมูลแบบ set-to-set โดยทั่วไปไม่ได้จริงๆ ต้องการหรือดูแลเกี่ยวกับการสั่งซื้อ ในทำนองเดียวกันการใช้หลักของการทดสอบการเป็นสมาชิกที่รวดเร็วนั้นไม่เชื่อเรื่องพระเจ้า

  • ที่กล่าวว่าฉันคิดว่ามีห้องพักเพื่อเพิ่มการใช้งานชุดทางเลือกเพื่อ PyPI โดยเฉพาะอย่างยิ่งมีบางกรณีพิเศษที่น่าสนใจสำหรับข้อมูลที่สามารถสั่งซื้อได้ซึ่งการดำเนินการ set-to-set สามารถเร่งความเร็วโดยการเปรียบเทียบช่วงของคีย์ทั้งหมด (ดูที่ https://code.activestate.com/recipes/230113-implementation-of- ชุดการใช้เรียงลำดับรายการ สำหรับจุดเริ่มต้น) IIRC, PyPI มีโค้ดสำหรับชุดตัวกรอง Bloom และ Set Hashing เรียบร้อยแล้ว

  • ฉันเข้าใจว่ามันเป็นเรื่องที่น่าตื่นเต้นที่ได้รับการยอมรับบล็อกหลักใน Python core แต่ไม่ควรเปิดให้มีช่องโหว่เพื่อให้มีส่วนร่วมในการเขียนที่สำคัญอื่น ๆ ของประเภทข้อมูลอื่นเว้นแต่ว่าเรามั่นใจว่าจะได้รับการรับประกัน

- Raymond Hettinger

จาก[Python-Dev] Python 3.6 dict มีขนาดกะทัดรัดและได้รับเวอร์ชันส่วนตัว และคำหลักได้รับคำสั่งตั้งแต่เดือนกันยายน 2016


2

การสนทนา

คำถามของคุณเป็นคำถามที่ดีและมีการพูดคุยกันมากใน python-devs เมื่อไม่นานมานี้ R. Hettinger แบ่งปันรายการเหตุผลในหัวข้อนั้น สถานะของปัญหาจะปรากฏขึ้นแบบเปิดตอนนี้ไม่นานหลังจากคำตอบอย่างละเอียดจาก T. Peters

ในระยะสั้นการใช้งานของ dicts ทันสมัยที่รักษาคำสั่งแทรกไม่ซ้ำกันและไม่ถือว่าเหมาะสมกับชุด โดยเฉพาะอย่างยิ่ง dicts ถูกใช้ทุกที่เพื่อเรียกใช้ Python (เช่น__dict__ในเนมสเปซของวัตถุ) แรงจูงใจหลักที่อยู่เบื้องหลัง dict สมัยใหม่คือการลดขนาดทำให้ Python โดยรวมมีประสิทธิภาพมากขึ้น ในทางตรงกันข้ามเซตนั้นมีความแพร่หลายน้อยกว่า dicts ภายในแกน Python ดังนั้นจึงเป็นการห้ามการปรับโครงสร้างดังกล่าว ดูเพิ่มเติมที่การพูดคุยของ R. Hettinger เกี่ยวกับการดำเนินการตามคำบอกในปัจจุบัน


มุมมอง

ลักษณะเรียงลำดับของชุดในหลามแนวการทำงานของชุดคณิตศาสตร์ ไม่รับประกันการสั่งซื้อ

แนวคิดทางคณิตศาสตร์ที่เกี่ยวข้องนั้นไม่มีการเรียงลำดับและมันก็แปลกที่จะกำหนดเช่นคำสั่ง - R. Hettinger

หากมีการแนะนำลำดับใด ๆให้กับชุดใน Python พฤติกรรมนี้จะสอดคล้องกับโครงสร้างทางคณิตศาสตร์ที่แยกจากกันอย่างสมบูรณ์นั่นคือชุดที่สั่ง (หรือ Oset) Osets เล่นม้วนแยกในคณิตศาสตร์โดยเฉพาะอย่างยิ่งใน combinatorics หนึ่งในโปรแกรมการปฏิบัติของ Osets เป็นที่สังเกตในการเปลี่ยนแปลงของระฆัง

มีชุดเรียงลำดับมีความสอดคล้องกับโครงสร้างข้อมูลมากทั่วไปและแพร่หลายว่าเลิกตรึงคณิตศาสตร์ที่ทันสมัยที่สุดคือทฤษฎีเซต ฉันส่งชุดที่ไม่มีการเรียงลำดับใน Python เป็นสิ่งที่ดี

ดูโพสต์ที่เกี่ยวข้องที่ขยายในหัวข้อนี้:

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