Tutorial

Dictionary ที่มากกว่า Dictionary บน Python ที่จะทำให้ง่ายขึ้นเยอะ

By Arnon Puitrakul - 17 กันยายน 2021 - 1 min read min(s)

Dictionary ที่มากกว่า Dictionary บน Python ที่จะทำให้ง่ายขึ้นเยอะ

หลาย ๆ คนที่เขียน Python มา น่าจะรู้จัก Dictionary กันเป็นอย่างดี มันเป็น Data Type ตัวนึงที่เราใช้กันอย่างแพร่หลายมาก ๆ เวลาเราใช้ ๆ ไปมันจะมีบางเคสที่เราอาจจะหงุดหงิดว่า ทำไมมันไม่เป็นแบบนั้น แบบนี้ ว้าาา หลายเคสมาก ๆ ทำให้วันนี้เราอยากจะพาไปทำความรู้จักกับ Specialised Dictionary บน Python กันว่า เขาได้ทำการ Implement Dictionary อะไรไว้ให้เราบ้าง

Dictionary คืออะไร ?

สำหรับคนที่ยังไม่รู้ว่า Dictionary คืออะไร ส่วนนี้เราจะมาปูพื้นฐานกันก่อน ตัว Dictionary เป็น Data Type ตัวนึงที่ใช้เก็บข้อมูลในรูปแบบความสัมพันธ์ Key และ Value อ่านแล้วอาจจะ งง ลองมาดูตัวอย่างกันดีกว่า

people_age = {
    "einstein" : 76,
    "edison": 84,
    "turing": 42
}

จากตัวอย่างด้านบน เราทำการสร้าง Dictionary ขึ้นมาใน Python ที่เก็บอายุของผู้คนเข้าไป โดยการจะบอก Python ว่า สิ่งที่เรากำลังสร้างคือ Dictionary เราจะกำกับด้วยเครื่องหมายปีกกาเสมอ พร้อมกับด้านใน เราจะสามารถเก็บได้หลาย ๆ Item

ซึ่งแต่ละ Item จะประกอบด้วยของ 2 อย่างคือ Key และ Value จากตัวอย่างเราจะเห็นได้ว่า เราทำการเก็บอายุของ 3 คนด้วยกัน โดยที่ด้านหน้า จะเป็น Key คั่นด้วยเครื่องหมาย Colon และก็จะเป็น Value

>>> people_age['einstein']
76

ในการเข้าถึงค่าต่าง ๆ เราสามารถทำได้ผ่านการเรียก Key ออกมา เราจะได้ Value เป็นความสัมพันธ์แบบ 1-1 นอกจากการเรียกค่าแล้ว เราก็ยังสามารถที่จะกำหนดค่าใหม่ได้เช่นกัน หรือแม้กระทั่งการสร้าง Key และ Value คู่ใหม่ก็สามารถทำได้ตรง ๆ เหมือนเรากำหนดค่าใหม่ได้เลยเช่นกัน

การใช้งานส่วนใหญ่ของ Dictionary จะเป็นส่วนของโปรแกรมที่เราจะต้อง Map ค่าต่าง ๆ ตัวอย่างเช่น เราอาจจะเก็บพวก Setting ของโปรแกรมเราไว้ มันก็จะมีเป็น ชื่อของ Setting และค่าของ Setting นั้น ๆ เราก็อาจจะให้ Key เป็นชื่อของ Setting และ Value เป็นค่าของ Setting ไป ก็จะทำให้เราสามารถเข้าถึง Setting ของโปรแกรมเราได้ง่ายมาก ๆ แล้ว

OrderedDict

from collections import OrderedDict
people_age = OrderedDict (
    einstein = 76,
    edison = 84,
    turing = 42
)

OrderDict เป็น Dictionary ประเภทหนึ่งที่มีลักษณะพิเศษคือ มันจะสนใจลำดับของ Key เป็นอย่างดีเลยละ โดยมันจะเรียงตาม Key อันไหนเข้ามาก่อน มันก็จะอยู่อันแรก อันไหนมาทีหลัง มันก็จะอยู่ข้างหลัง ไม่ขยับเหมือนกับ Dictionary ปกติ

เนื่องด้วยมันไม่ใช่ Primitive Data Type แต่เป็น Built-in Class ที่ Python Implement มาให้เรา เราเลยต้องทำการ Import มันเข้ามาใช้งานใน Script ของเราก่อน ส่วนเวลาเราเรียกใช้ แทนที่เราจะใช้แค่เครื่องหมายปีกกา เราก็ทำเหมือนกับเราสร้าง Object ปกติได้เลย ส่วนการ Init ข้อมูล แทนที่เราจะใช้เครื่องหมาย Colon ในการคั่น เราก็ทำเป็น Argument แทน โดยการใช้เท่ากับเหมือนกับตอนที่เราสร้าง Object ตามปกติได้เลย

อย่างที่บอกว่า OrderedDict มันจะสนใจลำดับของ Key เป็นหลัก ทำให้การนำไปใช้จะเป็น Application ที่เราต้องการเก็บลำดับของ Key จริง ๆ ถ้าคิดเร็ว ๆ ก็น่าจะเป็น To-Do List ที่เราต้องการบอกว่า เราจะทำอะไรก่อนหลัง ก็เป็นตัวอย่างที่ดีของการใช้ OrderedDict เลย

defaultdict

เวลาเราใช้งาน Dictionary แบบปกติ บางที เราจะเจออาการว่า เราเรียก Key ที่มันไม่มีอยู่จริง ทำให้โปรแกรมของเรา Crash เฉย ถ้าเราไม่ได้เขียน Exception ดักไว้ ซึ่งถ้าเราเขียนดักไว้ เราอาจจะดักด้วยการให้มันโยนค่าอะไรบางอย่างกลับมา เป็นเหมือนค่า Default อะไรบางอย่าง เพื่อให้โปรแกรมของเราทำงานได้ แน่นอนว่า Python ก็มี Dictionary ที่ทำอะไรแบบนี้มาให้เราคือ defaultdict

from collections import defaultdict

people_age = defaultdict ( lambda: 0,
    einstein = 76,
    edison = 84,
    turing = 42
)

การใช้งานก็เหมือนกันเลย เพราะมันเป็น Class เหมือนกัน เริ่มจาก Import เข้ามาเหมือนเดิมทุกอย่าง แต่ ๆ สังเกตที่ Argument แรกดี ๆ เราจะใช้เป็น Anonymous Function เข้ามาช่วย เพราะใน defaultdict ตามชื่อเลย เราจะต้องกำหนด Default Value ด้วย แต่แทนที่มันจะให้เรากำหนดเป็นค่านิ่ง ๆ เลย เราจะต้องกำหนดเป็น Function ที่ Return ค่ากลับไปแทน ตัวอย่างนี้ เราต้องการให้ถ้า เราเรียก Key ที่ไม่มี เราก็จะให้อายุเป็น 0 เลย เราก็เลยสร้าง Anonymous Function ที่ไม่ว่าจะอะไรก็ตาม มันก็จะคืน 0 กลับมาเสมอนั่นเอง

>>> people_age['turing']
42

>>> people_age['curie']
0

ถ้าเราลองเรียกค่าออกมาดู ค่าแรกที่เราเรียกออกมา ก็จะได้ตามปกติเหมือนกับเราใช้งาน Dictionary ทั่ว ๆ ไปเลย แต่ถ้าเราลองเรียก Key ที่มันไม่มีอยู่ แทนที่เราจะได้ Error กลับมา เราก็จะได้ 0 ซึ่งเป็น Default Value ที่เรากำหนดไว้กลับมานั่นเอง ก็จะทำให้เราไม่ต้องมานั่งเขียนพวก Exception ดักไว้แล้ว

Bonus: MappingProxyType

ตัวสุดท้ายถือว่าเป็น Bonus ละกัน เพราะมันเป็นเหมือน Wrapper ที่มาครอบ Dictionary อีกที ตัว MappingProxyType เป็น Typing ประเภทนึงที่เมื่อเราเอามาครอบพวก Dictionary แล้ว มันจะทำให้ Dictionary นั้น ๆ มีสมบัติ Immutable ไปเลย หรือ ก็คือแก้อะไรไม่ได้นั่นเอง

from types import MappingProxyType

people_age = MappingProxyType({
    "einstein" : 76,
    "edison": 84,
    "turing": 42
})

จากด้านบน เราทำการ Import MappingProxyType เข้ามา และเราก็เอามาครอบ Dictionary ธรรมดานี่ละ ก็พร้อมใช้งานแล้ว

>>> people_age['edison']
84

เมื่อเราลองเรียกค่าออกมา เราก็จะเห็นได้เลยว่า ค่าก็ออกมาได้ปกติ และถูกต้อง

>>> people_age['edison'] = 50

TypeError: 'mappingproxy' object does not support item assignment

แต่เมื่อเราพยายามที่จะเปลี่ยนแปลงค่าใหม่เข้าไป มันก็จะไม่ยอม และโยน Exception กลับมาให้เฉยเลย โดยบอกว่า Object นี้ไม่รองรับการ Assign ค่าใหม่ คิดเหมือนกันมั้ย B2

>>> people_age['curie'] = 67

TypeError: 'mappingproxy' object does not support item assignment

ใช่แล้ว ละ B1 ตัว MappingProxyType มันไม่รองรับการ Assign ค่าทุกอย่างจริง ๆ ไม่ว่า Key นั้นมันจะมีอยู่ใน Dictionary หรือไม่ มันทำให้ Object น้ัน ๆ Immutable โดยสมบูรณ์แน่นอน ทำให้เรามั่นใจได้เลยว่า ระหว่างที่เรารันโปรแกรม ข้อมูลชุดนี้จะไม่สามารถถูกเปลี่ยนได้จาก Code ของเราแน่นอน

เอาจริง ๆ เราก็ไม่ค่อยได้ใช้เท่าไหร่หรอก แต่ถ้าเป็นเคสที่ต้องใช้ และเจอบ่อย ๆ เราจะเอาไปใช้กับพวกค่าคงที่ต่าง ๆ ซะเยอะ เพราะใน Python มันไม่มีพวก Constant หรือค่าคงที่เหมือนกับภาษาอื่น ๆ เช่น C การใช้ MappingProxyType ก็ทำให้เราปลอดภัยว่า มันจะไม่มี Code ส่วนไหนเลย ที่จะเข้าไปยุ่งกับค่าคงที่ของเรา ตัดปัญหาไปได้เรื่องนึงเลย

สรุป

Dictionary เป็น Data Type ที่เราใช้กันเยอะมาก ๆ ใน Python เองก็มีหลาย ๆ ท่าให้เราเล่นกับ Dictionary มาก ๆ หรือบางคนก็เล่น Implement เองซะเลย อย่างพวก SortedDictionary อะไรพวกนั้นก็มีเหมือนกัน ถ้าสนใจลองเข้าไปหาอ่านได้เยอะมาก ๆ โดยเฉพาะใน Stackoverflow มีเคสแปลก ๆ เยอะมาก อ่านแล้วก็เออ แบบนี้ก็ได้เหรอหลายรอบมาก ๆ ได้ความรู้เยอะดี