หลักสูตรการพัฒนา Python ขั้นสูง
บทที่
>
ระดับ

โมดูลการอนุกรมข้อมูล
โมดูล Struct

วัตถุประสงค์

ตั้งค่าการแยกข้อมูลขั้นสุดท้ายสำหรับพื้นที่เพาะปลูกใหม่โดยใช้โมดูล struct

ทางปลายถนนจะมีสถานีบริการที่ดูแลพื้นที่เพาะปลูกใหม่ที่สร้างเสร็จและพืชผลที่ปลูกแล้ว ที่นี่เราจะทำการตรวจสอบและประมวลผลข้อมูลของพืชผลที่ปลูกแล้วและการคาดการณ์ผลผลิตของพื้นที่เพาะปลูก เช่นเดียวกับเลเวลอื่นในบทนี้ เราจะทำงานกับ serialization และ deserialization ของข้อมูลโดยแนะนำโมดูลสุดท้ายคือ struct

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

  • struct.calcsize(): ใช้ตรวจสอบจำนวนไบต์ที่บรรจุตาม format string ที่กำหนด โดยรับอาร์กิวเมนต์หนึ่ง (1) ตัวคือฟอร์แมตที่คุณต้องการตรวจสอบขนาดในหน่วยไบต์ ฟอร์แมตต่างๆ ที่เราจะใช้มีดังนี้:
    • integer: ใช้ 'i' เป็นฟอร์แมตสำหรับข้อมูลตัวเลขจำนวนเต็ม
    • float: ใช้ 'f' เป็นฟอร์แมตสำหรับข้อมูลตัวเลขทศนิยม
    • double: ใช้ 'd' เป็นฟอร์แมตสำหรับข้อมูลตัวเลขทศนิยมที่ซับซ้อนมากขึ้นซึ่งฟอร์แมต float ไม่เพียงพอ
  • struct.pack(): บรรจุข้อมูลเป็นไบนารีตามฟอร์แมตที่คุณเลือก โดยรับอาร์กิวเมนต์สองตัวขึ้นไป คือฟอร์แมตและค่าที่ต้องการบรรจุ ฟอร์แมตให้ใช้ตามที่ระบุไว้ข้างต้นและต้องใส่ให้ตรงกับจำนวนอาร์กิวเมนต์ที่เพิ่มเข้ามา
  • struct.unpack(): แปลงข้อมูลไบนารีที่บรรจุแล้วกลับมาเป็นข้อมูลเดิม รับอาร์กิวเมนต์สองตัวคือฟอร์แมตที่ใช้บรรจุและข้อมูลไบนารีที่ต้องการแปลงกลับ
  • struct.iter_unpack(): ทำงานเช่นเดียวกับ struct.unpack() แต่จะวนลูปผ่านแต่ละบล็อกข้อมูลทีละชุด
  • struct.pack_into(): เวอร์ชันขั้นสูงของ struct.pack() รับอาร์กิวเมนต์สี่ตัว ได้แก่ ฟอร์แมต, บัฟเฟอร์ข้อมูล, ตำแหน่งในบัฟเฟอร์ที่ต้องการบรรจุ, และค่าที่จะบรรจุลงไป
  • struct.unpack_from(): เวอร์ชันขั้นสูงของ struct.unpack() รับอาร์กิวเมนต์สามตัว ได้แก่ ฟอร์แมต, บัฟเฟอร์ข้อมูล, และตำแหน่งในบัฟเฟอร์ที่ต้องการแปลงกลับ ช่วยให้คุณแปลงข้อมูลเฉพาะส่วนที่ต้องการได้

เดินไปยังเครื่องหมาย X สีสว่างในสถานีบริการแล้วหันหน้าเข้าหาเคาน์เตอร์ สร้างตัวแปรสามตัวชื่อ integer, float และ double เราจะใช้ตัวแปรเหล่านี้เพื่อตรวจสอบขนาดในหน่วยไบต์ของแต่ละฟอร์แมตด้วยฟังก์ชัน struct.calcsize() สำหรับตัวแปร integer ใช้ 'i' เป็นอาร์กิวเมนต์, สำหรับ float ใช้ 'f' และสำหรับ double ใช้ 'd' เช่นตัวอย่าง: integer = struct.calcsize('i') จากนั้นนำตัวแปรทั้งสามไปใส่ในฟังก์ชัน write() ที่มีให้แล้ว

เดินไปยังเครื่องหมาย X สีทองและใช้ฟังก์ชัน read() เพื่อเก็บข้อมูลพื้นที่เพาะปลูกใหม่ จดบันทึกจุดข้อมูลและฟอร์แมตที่ต้องใช้ในภายหลัง ได้แก่ Resources, Size และ Estimate เมื่อจดเรียบร้อยแล้ว ให้เดินไปยังเครื่องหมาย X สีสว่างบนพรมสีน้ำเงินและสร้างตัวแปรชื่อ blue_data

ให้ตัวแปร blue_data เก็บค่าที่ส่งกลับจากฟังก์ชัน struct.pack() โดยกำหนดอาร์กิวเมนต์เป็นฟอร์แมตและค่าที่คุณจดมา เมื่อเขียนฟอร์แมต คุณต้องรวมชนิดฟอร์แมตเข้าด้วยกันเป็นหน่วยเดียว เช่น ฟอร์แมต integer ใช้ 'i' หากมีข้อมูล integer สามตัวก็เขียนว่า 'iii' หากเป็น integer, float, double ก็เขียน 'ifd' และเมื่อเพิ่มค่าก็ใส่เป็นอาร์กิวเมนต์แยกกัน ตัวอย่างโดยรวม:

data_1 = 8 # is an integer data_2 = 2.25 # is a float data_3 = 900.702938103 # is a double blue_data = struct.pack('ifd', data_1, data_2, data_3)

ฟังก์ชัน struct.pack() มีความยืดหยุ่นสูง ช่วยให้คุณกำหนดฟอร์แมตและบรรจุข้อมูลหลายจุดพร้อมกันตามต้องการ ใช้ข้อมูลที่อ่านมาแล้วตามตัวอย่างข้างต้นเป็นฐาน และใช้ฟังก์ชัน display() โดยส่ง blue_data เพื่อดูข้อมูลที่บรรจุ

เดินไปยังเครื่องหมาย X สีเข้มบนพรมสีน้ำเงินแล้วหันหน้าเข้าหาเทอร์มินัล สร้างตัวแปรชื่อ blue_unpack และเก็บค่าจากฟังก์ชัน struct.unpack() โดยใช้ฟอร์แมตเดียวกับที่บรรจุและตัวแปร blue_data เป็นอาร์กิวเมนต์ ฟอร์แมตเขียนเหมือน struct.pack() เพื่อให้คุณแปลงข้อมูลกลับมาในรูปเดิม ใช้ฟังก์ชัน write() โดยส่ง blue_unpack เพื่อตรวจสอบข้อมูลที่บรรจุไว้ก่อนหน้านี้

ภายใต้ตำแหน่งเดียวกัน เราจะใช้ฟังก์ชัน struct.iter_unpack() ซึ่งใช้พารามิเตอร์เหมือนกับ struct.unpack() แต่เขียนในรูปของลูป for ช่วยให้เราวนอ่านข้อมูลทีละบล็อก โค้ดตัวอย่าง:

for values in struct.iter_unpack(-insert value-, -insert value-): player.speak(values)

เราจะใช้ฟังก์ชัน speak() เพื่อแสดงค่า สำหรับอาร์กิวเมนต์ที่เว้นว่างใน struct.iter_unpack() ให้ใช้ค่าเดียวกับที่ใช้ใน struct.unpack() เพราะเป็นฟังก์ชันที่คล้ายกันในแง่ของการใช้งาน

เดินไปยังเครื่องหมาย X สีทองบนพรมสีแดงแล้วหันหน้าเข้าหาเคาน์เตอร์ ใช้ read() เพื่อตรวจสอบปริมาณพืชผล จดบันทึกจำนวนและฟอร์แมตของแต่ละพืช แล้วเดินไปยังเครื่องหมาย X สีสว่างบนพรมสีแดงและสร้างออบเจกต์ชื่อ buffer โดยกำหนดค่าเป็น bytearray(16) นี่คือคอลเลกชันของไบต์ที่เราสามารถกำหนดตำแหน่งก่อนบรรจุข้อมูลได้ ในกรณีนี้มันทำงานเหมือนธนาคารข้อมูลที่คุณสามารถเก็บข้อมูลไว้เอง จำนวน 16 คือขนาดของไบต์ที่บัฟเฟอร์จะรองรับ

ใช้ฟังก์ชัน struct.pack_into() เพื่อเติมข้อมูลลงในออบเจกต์ buffer ที่สร้างขึ้น โดยไม่ต้องสร้างตัวแปรเก็บผลลัพธ์ เพราะฟังก์ชันจะใส่ค่าโดยตรงใน buffer เราจะใช้ฟังก์ชันนี้กับค่าที่จดไว้ก่อนหน้านี้และใส่อาร์กิวเมนต์ให้ตรงตามนั้น

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

struct.pack_into('i', buffer, 0, 82)

buffer มีขนาด 16 ไบต์ ตามที่ระบุไว้ ในโค้ดข้างต้น ฟอร์แมต integer ใช้ 4 ไบต์และถูกใส่ที่ตำแหน่ง 0 ซึ่งหมายความว่าหลังจากใส่ข้อมูลแล้วจะเหลือไบต์ว่างใน buffer อีก 12 ไบต์ เนื่องจากตำแหน่ง 0-3 ถูกใช้โดยค่าตัวเลข (ในตัวอย่างคือ 82) ทำเช่นนี้กับข้อมูลพืชผลทั้งสามจุดที่จดไว้ จากนั้นใช้ฟังก์ชัน display() โดยส่ง buffer เพื่อแสดงข้อมูลที่บรรจุ

เดินไปยังเครื่องหมาย X สีเข้มบนพรมสีแดงแล้วหันหน้าเข้าหาเทอร์มินัล ที่นี่เราจะทำการแปลงข้อมูลกลับมาเพื่อตรวจสอบความถูกต้อง สร้างตัวแปรสามตัวชื่อ lettuce, carrots และ melons เราจะใช้แยก unpack ข้อมูลแต่ละจุด สำหรับแต่ละตัวแปร ให้เก็บค่าที่ส่งกลับจาก struct.unpack_from() โดยกำหนดอาร์กิวเมนต์คือฟอร์แมต, ออบเจกต์ buffer และตำแหน่งในบัฟเฟอร์ที่ต้องการแปลงกลับ ตัวอย่าง:

lettuce = struct.unpack_from('i', buffer, 0)

ข้อมูลนี้ตรงกับตัวอย่างการบรรจุก่อนหน้า ให้ทำเช่นเดียวกันกับตัวแปรอีกสองตัว จากนั้นส่ง lettuce, carrots และ melons เข้าไปในฟังก์ชัน write() ที่เตรียมไว้เพื่อจบเลเวล

หนังสือโค้ด