2.37. Named tuples และ deques¶
Lists, tuples, dictionaries และ sets ครอบคลุมความต้องการข้อมูลส่วนใหญ่ คอนเทนเนอร์อีกสามตัวในโมดูล collections เหมาะกับปัญหาเฉพาะที่ built-ins จัดการได้ไม่ดีนัก
2.37.1. namedtuple -- เรคคอร์ดที่มีชนิดข้อมูลโดยไม่ต้องสร้างคลาส¶
tuple ธรรมดาเก็บค่าตามตำแหน่ง นั่นเหมาะสำหรับ 2- หรือ 3-tuple ขนาดเล็กที่ใช้ชั่วคราว แต่เมื่อเกินนั้น point[0] และ point[1] เริ่มสื่อความหมายไม่ชัดเจน collections.namedtuple() ส่งคืน subclass tuple ใหม่ที่ฟิลด์มีชื่อ:
>>> from collections import namedtuple
>>> Reading = namedtuple('Reading', ('temp', 'humidity', 'ts'))
>>> r = Reading(22.5, 41.0, 137204)
>>> r.temp
22.5
>>> r.humidity
41.0
>>> r[0]
22.5
อาร์กิวเมนต์ fields คือลำดับของสตริงชื่อ (หรือสตริงเดียวที่คั่นด้วยช่องว่างใน CPython; MicroPython เข้มงวดกว่า -- ต้องส่ง tuple หรือ list)
ทำไมต้องใช้ namedtuple แทนคลาส?
มันคือ tuple การวนซ้ำ, การ unpack, ความเท่ากัน, การแฮช และการใช้เป็น dict key ล้วนใช้งานได้ฟรี
มันไม่เปลี่ยนแปลงได้ การกำหนดค่าใหม่
r.temp = ...จะเกิดAttributeErrorซึ่งเป็นสิ่งที่ต้องการสำหรับชนิดเรคคอร์ดใช้ RAM น้อยกว่า instance ของคลาสที่มีฟิลด์เดียวกัน -- การจัดเก็บ tuple เป็นแบบต่อเนื่อง ไม่มี
__dict__
เมื่อเปรียบกับคลาสที่เทียบเท่า การประกาศ namedtuple ใช้บรรทัดเดียว ข้อแลกเปลี่ยนคือฟิลด์เป็นแบบอ่านอย่างเดียว -- การ "เปลี่ยน" การอ่านค่าต้องสร้างใหม่
2.37.2. deque -- บัฟเฟอร์วงแหวนที่มีขนาดจำกัด¶
list เร็วที่ ปลาย (append / pop) และช้าที่ ต้น (insert(0, ...) / pop(0) ต้องเลื่อนทุกองค์ประกอบอื่น) collections.deque เร็วที่ทั้งสองด้าน -- เป็นบัฟเฟอร์วงแหวนที่จัดดัชนีด้วยตัวชี้หัวและท้าย ดังนั้น append และ pop ที่ฝั่งใดก็ทำงานในปริมาณงานคงที่เดิมโดยไม่ขึ้นกับจำนวนรายการใน deque
การสร้างใน MicroPython ต้องการทั้ง iterable เริ่มต้น และ ความยาวสูงสุด ตามลำดับนั้น:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
เมื่อ deque ที่มีขนาดจำกัดเต็ม ทุก append จะทิ้งรายการเก่าที่สุด นั่นทำให้ deque เหมาะอย่างยิ่งสำหรับ "N ตัวอย่างล่าสุด", "N บรรทัดล็อกล่าสุด" หรือหน้าต่างเลื่อนใดๆ ที่ไม่ต้องการเติบโตตลอดไป
เมธอดที่เปิดเผยบน deque ของ MicroPython มีน้อยอย่างตั้งใจ:
append(x)-- เพิ่มไปทางขวาappendleft(x)-- เพิ่มไปทางซ้ายextend(iterable)-- ผนวกแต่ละรายการจาก iterablepop()-- ลบและส่งคืนปลายขวา เกิดIndexErrorเมื่อว่างเปล่าpopleft()-- ลบและส่งคืนปลายซ้าย
การละเว้นที่สังเกตได้จาก deque ของ CPython: ไม่มี clear, count, index, remove, reverse, rotate, แอตทริบิวต์ maxlen หรือ __contains__ การวนซ้ำและการจัดดัชนีด้วย subscript ใช้งานได้:
>>> events[0]
3
>>> for e in events:
... print(e)
การใช้งานทั่วไป: เก็บการอ่านค่าจากเซนเซอร์ล่าสุดไม่กี่ครั้งเพื่อตรวจจับการเปลี่ยนแปลงแนวโน้ม:
history = deque((), 10)
def push(reading):
history.append(reading)
if len(history) == 10 and history[-1] > 2 * history[0]:
print('reading is climbing')
2.37.3. OrderedDict -- เมื่อลำดับเป็นส่วนหนึ่งของความเท่ากัน¶
dict ปกติสงวนลำดับการแทรกตั้งแต่ MicroPython 1.13 และ CPython 3.7 นั่นครอบคลุมเหตุผลที่พบบ่อยที่สุดที่คนใช้ collections.OrderedDict
สิ่งที่ OrderedDict ยังให้คุณที่ dict ธรรมดาไม่มี:
ความเท่ากันของ
OrderedDictพิจารณาลำดับ dict ธรรมดาสองตัวเปรียบเท่ากันเมื่อมีคู่ key/value เหมือนกัน โดยไม่คำนึงถึงลำดับการแทรก instance ของOrderedDictสองตัวเท่ากันก็ต่อเมื่อคู่ข้อมูลอยู่ในลำดับเดียวกัน:>>> from collections import OrderedDict >>> OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) False >>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1} True
OrderedDictมีประโยชน์เมื่อคุณกำลัง serialize การตั้งค่าเป็นรูปแบบที่ ใส่ใจ ลำดับ key (TOML, ผู้บริโภค YAML บางราย) หรือเมื่อคุณต้องการคำใบ้ในเอกสารที่ชัดเจนว่าลำดับมีความสำคัญสำหรับผู้อ่านโค้ดของคุณ
สำหรับโค้ดทั่วไป ให้ใช้ dict ในตัว ใช้ OrderedDict ก็ต่อเมื่อ semantics ที่ลำดับเป็นส่วนหนึ่งของความเท่ากัน หรือคุณค่าทางเอกสารให้ประโยชน์จริงๆ
คอนเทนเนอร์ทั้งสามตัวต่างมีการใช้งานเฉพาะที่แคบ Named tuples แทนที่คลาสเรคคอร์ดที่เขียนเอง deques แทนที่ list สำหรับคิวที่มีขนาดจำกัด ordered dicts ทำให้ลำดับการแทรกเป็นส่วนหนึ่งของสัญญา