งูขดตัวต้นฉบับกำลังหมุนกรอบมองเมล็ดชุดเดิมจากช่องเล็กให้เป็นช่องกว้างโดยไม่ย้ายเมล็ด

Python memoryview.cast: เปลี่ยนมุมมอง bytes โดยไม่ copy

พี่โชว์Engineering

บัฟเฟอร์จากไฟล์หรือ network มักมาเป็น bytes แต่ algorithm อยากอ่านเป็นจำนวนเต็ม การ unpack ทั้งก้อนสร้าง object ใหม่จำนวนมากครับ memoryview.cast เปลี่ยน “เลนส์” ที่มอง memory เดิมโดยไม่ copy เหมือนเปลี่ยนไม้บรรทัดจากขีดละหนึ่งไบต์เป็นช่องละสี่ไบต์

WHY: zero-copy ช่วยเมื่อบัฟเฟอร์ใหญ่และอยู่ไม่นาน

memoryview ยืม buffer protocol จากเจ้าของข้อมูล การ cast เปลี่ยน format และ shape ของ view แต่ไม่ได้แปลง byte order ให้เรา ขนาดรวมต้องหารขนาดสมาชิกใหม่ลงตัว และอย่างน้อยหนึ่งด้านของการ cast ต้องเป็น byte format ตามข้อกำหนด API

import array
import unittest


def as_native_unsigned_ints(buffer):
    view = memoryview(buffer)
    if view.nbytes % 4:
        raise ValueError("buffer length must be a multiple of four bytes")
    return view.cast("I")


class MemoryViewCastTests(unittest.TestCase):
    def test_view_reads_same_storage(self):
        owner = array.array("I", [10, 20, 30])
        byte_view = memoryview(owner).cast("B")
        integers = as_native_unsigned_ints(byte_view)
        self.assertEqual(list(integers), [10, 20, 30])
        owner[1] = 99
        self.assertEqual(integers[1], 99)

    def test_rejects_partial_element(self):
        with self.assertRaisesRegex(ValueError, "multiple of four"):
            as_native_unsigned_ints(b"abc")


if __name__ == "__main__":
    unittest.main()

ขอบเขตที่ต้องพูดให้ครบ

ตัวอย่างใช้ native unsigned int จึงขึ้นกับขนาดและ byte order ของเครื่อง หาก protocol ระบุ big-endian ห้ามถือว่า cast("I") แปลงให้ ต้อง decode ตาม protocol อย่างชัดเจน memoryview ยังยึดเจ้าของ buffer ไว้ และ view ที่ writable สามารถแก้ข้อมูลต้นฉบับได้ จึงควรคืน read-only view หรือ copy เมื่อส่งข้าม trust boundary

Recap

  • ตรวจจำนวน byte ให้ลงตัวกับ format ใหม่
  • แยก native layout ออกจาก network byte order
  • ระวัง lifetime และ mutability ของ buffer เจ้าของ
  • benchmark allocation จริงก่อนเพิ่มความซับซ้อน