วันอาทิตย์ที่ 24 พฤศจิกายน พ.ศ. 2556

การใช้ Constants, User Defined Data Type, และ Enumerations ในภาษา Python

เนื่องจากไม่ค่อยได้เขียนโปรแกรมเป็นเรื่องเป็นราวเท่าไหร่ พอได้เริ่มเขียนภาษา Python ก็เลยนึกถึงวิธีประกาศค่าต่าง ๆ จากที่เคยเขียนมา แต่สิ่งที่แตกต่างก็คือส่วนใหญ่ที่เขียนมาจะต้องมีการประกาศชนิดของข้อมูลที่จะจัดเก็บก่อนที่จะนำไปใช้เสมอ หรือที่เรียกว่า Static Type แต่พอมาคราวนี้สามารถใช้งานจัดเก็บข้อมูลได้ทันทีโดยไม่ต้องประกาศอะไรเลย หรือที่เรียกว่า Dynamic Type เรื่องข้อดีข้อเสียคงไม่ต้องกล่าวถึง เนื่องจากถึงจะอยากใช้ Static Type ไปก็ใช้ไม่ได้เนื่องจากตัวภาษา Python ไม่สนับสนุนนั่นเอง แต่อย่างไรก็ดี ด้วยความอยากใช้เลยต้องไปค้นหาวิธีที่จะใช้งานบางคุณสมบัตินั่นคือ Enumeration, Constant และ User-Defined Type เรามาเริ่มกันเลย

Enumeration

คุณสมบัตินี้มีใน Python เกือบจะดีใจละ แต่ว่าเริ่มมีในเวอร์ชั่น 3.4 ตาม PEP 435 ซึ่งไม่ใช่เวอร์ชั่นที่กำลังใช้อยู่ เฮ้อ....เศร้า แต่ยังไงก็ต้องเปลี่ยนไปใช้รุ่นใหม่อยู่ดี ตอนนี้ขอรอ Package หรือ Module ต่าง ๆ ที่มีสามารถทำงานได้บนรุ่น 3 เยอะ ๆ ก่อนค่อยย้ายไป แล้วถ้างั้นตอนนี้ทำยังไงดีล่ะ 

ตัวอย่างการใช้งาน Enumeration ในรุ่น 3.4
from enum import Enum
    Animal = Enum('Animal', 'ant bee cat dog')
หรือจะเขียนแบบข้างล่างนี้ก็ได้
class Animals(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

ในเวอร์ชั่น 2.7 มี Python Package(PyPi) ที่ชื่อว่า enum ให้ใช้ แต่ว่าเหมือนทำงานได้เป็นแบบ Automatic Enumeration แต่ลองค้นไปเจอในเว็บ Stackoverflow มีคนถามเรื่องนี้อยู่เหมือนกันแถมมีทางออกให้ด้วย ... ง่ายเลยไม่ต้องคิด (เอ..... ดีหรือไม่ดีเนี่ย)
def enum(**enums):
    return type('Enum', (), enums)
การใช้งาน
Numbers = enum(ONE=1, TWO=2, THREE='three')
Numbers.ONE #ได้ค่าเป็น 1
Numbers.TWO # ได้ค่าเป็น 2
Numbers.THREE # ได้ค่าเป็น 'three'
ถ้าจะทำเป็น Automatic Enumeration ก็จะเป็น
# ใช่ * คือส่งค่าเป็น tuples ส่วนน ** เป็นการส่งค่าแบบ dictionary
def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)
การใช้งาน
Numbers = enum('ZERO', 'ONE', 'TWO')
Numbers.ZERO #ได้ค่า 0
Numbers.ONE #ได้ค่า 1
การแปลงจากค่าไปเป็นชื่อก็สามารถทำได้ดังนี้
def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)
เหมาะกับการนำชื่อ enum นั้นออกมาแสดงผลแต่อาจเกิดข้อผิดพลาด KeyError ถ้าค่าที่ให้หาไม่ได้ถูกแมพไว้กับชือ
Numbers.reverse_mapping['three'] #ได้ชื่อ 'THREE'
เมื่อลองดูแล้วก็ค่อนข้างงงกับความยืดหยุ่นของ Python อยู่เหมือนกันที่สามารถเขียนได้ทั้งรูปแบบ Functional และ Object แต่ได้ class ออกมาเหมือนกัน เอาเป็นว่าทำได้ละกัน

Constant

การประกาศค่าคงที่ก็ไม่สามาถทำได้ตรง ๆ ใน Python เช่นกัน แต่ถ้าต้องการใช้งานก็ยังสามารถประยุกต์ให้เราสามารถสร้างค่าคงที่ได้ในโปรแกรมโดยสร้างโมดูลดังนี้
#สร้าง constant.py ด้วยโค้ดต่อไปนี้
class _const:
    class ConstError(TypeError): pass
    def __setattr__(self, name, value):
        if self.__dict__.has key(name):
            raise self.ConstError, "Can't rebind const(%s)"%name
        self.__dict__[name]=value
import sys
sys.modules[__name]=_const()
จากโมดูลข้างต้นจะสามารถสร้างค่าคงที่ได้ดังนี้
#ในไฟล์ที่ต้องการใช้งานให้เรียกใช้โมดูลที่สร้างไว้ก่อนหน้า
import const
#ประกาศค่าคงที่โดยใช้คำสั่ง
const.magic = 23
#ถ้ามีการประกาศค่าซ้ำโปรแกรมจะขึ้น Error (const.ConstError) โดยลองประกาศค่าซ้ำ
const.magic = 88 

User-Defined Data Type 

เรื่องนี้ก็เช่นเดียวกับเรื่องก่อนหน้าคือ Pyhton ไม่มีให้ประกาศกันตรง ๆ ต้องใช้การเลี่ยงไปใช้ dictionary หรือใช้ class คล้ายกับข้างบน ถ้าหากใช้ dictionary ก็จะได้
Person = {'firstname':None, 'lastname':None}
การใช้งานทำได้โดย
Person['firstname']="firstname"
Person['lastname']="lastname"
ถ้าจะใช้้ class สำหรับปัญหานี้จะออกมาในรูปแบบ
class Person(object):
    firstname=None
    lastname=None
การใช้งานจะเป็นดังนี้
Person.firstname="firstname"
Person.lastname="lastname"
ก็ต้องขอขอบคุณแหล่งข้อมูลต่าง ๆ ที่ยกตัวอย่างมา เป็นประโยชน์อย่างมาก สำหรับบทความนี้ก็ขอจบไว้แต่เพียงเท่านี้