Tutorial

รวม 3 Decorator ที่ใช้บ่อยใน Python

By Arnon Puitrakul - 02 กันยายน 2021

รวม 3 Decorator ที่ใช้บ่อยใน Python

ตอนก่อน เรามาเล่าเรื่อง Decorator ใน Python กันไปแล้ว มาในตอนนี้เราอยากจะมาเล่าถึง 3 Decorator ที่เราใช้บ่อย ๆ ส่วนใหญ่จะเป็น Snippet เขียนเองทั้งนั้น วันนี้มาดูกันว่าจะมี Decorator ตัวไหนบ้าง

timeme

ตัวแรกที่เราใช้บ่อยมาก ๆ คือการวัดเวลาในการทำงานของส่วนต่าง ๆ ของ Code โดยเฉพาะช่วงทำ Thesis อยากรู้ว่า Function มันใช้เวลาเท่าไหร่ เลยลองเขียนเป็น Decorator ง่าย ๆ ขึ้นมาตัวนึง เพื่อวัดเวลา

import time
import functools

def timeme (func) :
    
    @functools.wraps(func)
    def measure_time (*args,**kwargs) :
        start_time = time.time()
        result = func(*args,**kwargs)
        elapsed = time.time() - start_time
        
        print(func.__name__, 'ran for', elapsed, 'sec(s)')
        return result        

    return measure_time

@timeme
def cal_result (max_num) :
    result = 0
    for num in range(max_num) :
        result += num

cal_result(10000000)

เราสร้างเป็น Wrapper Function ง่าย ๆ เอา ส่วนของตัววัดเวลามาคล่อมเอาเลย ทำให้เราได้เวลาในการรัน Function นั้น ๆ ออกมาได้เลย หรือจริง ๆ แล้ว ถ้าเราไม่อยากให้มัน Print ออกมา เมื่อ Function รันเสร็จ เราอาจจะเก็บใส่ไว้ใน Decorator แล้วเราก็ค่อยเรียกออกมาเวลาที่เราต้องการก็ทำได้เหมือนกัน แต่ปกติเวลาเราใช้งาน เราจะใส่เพื่อ Test เฉย ๆ เลยให้มัน Print ออกมาเลย จะได้ง่าย ๆ

dataclass

อันถัดไปใช้บ่อยไม่แพ้กันคือ dataclass ตัวนี้เราไม่ต้องเขียนเอง เพราะมันมีมากับ Python แล้ว ก่อนจะเข้าเรื่องของ Decorator นี้ เราจะต้องเกริ่นก่อนว่า เวลาเราเขียน Class เราจะต้องเขียนสิ่งที่เรียกว่า Constructor ซึ่งถ้าเราจะต้องทำอะไรในนั้นนอกจาก การ Init พวก Attribute ต่าง ๆ เขียนเองมันก็โอเคแหละ แต่ถ้าเราทำแค่นั้นละ เขียนแค่นั้นทุกครั้งมันก็น่าเบื่อแย่เลย dataclass เข้ามาช่วยแก้ปัญหาเรื่องนี้ได้เป็นอย่างดีเลย

from dataclass import dataclass

@dataclass
class Person:
    name: str
    surname: str
    age: int = 1
        
    def full_name(self) -> str:
        return name + ' ' + surname

thomas = Person('Thomas', 'Edison', 84)

จะเห็นว่า ใน Class Person เราไม่ได้เขียน Constructor เลย แต่ตอนที่เราเอา Class ออกมาสร้างเป็น Object เราใช้ Constructor ที่ใส่ของเข้าไป 3 อย่าง ซึ่งเราไม่ได้ประกาศเอง เป็นสิ่งที่ dataclass มันทำให้เราเอง จะเห็นได้เลยว่า การใช้ dataclass ทำให้การสร้าง Constructor ง่ายขึ้นเยอะ เพราะเราไม่ต้องเขียนเอง ฮ่า ๆ

Singletons

และตัวสุดท้ายของวันนี้คือ Singletons ก่อนเราจะไปเข้าใจว่ามันทำอะไร เรามาเข้าใจ Concept ของ Singleton อย่างง่าย ๆ ก่อน ตามชื่อของมันเลยคือ Single ที่ไม่ได้แปลว่าโสด แต่แปลว่า หนึ่ง หรือก็คือ เป็น Object หรือตัวแปรอะไรบางอย่างที่เราจะกำหนดให้มันมีเพียงตัวเดียวเท่านั้น เช่น Pointer ในการอ่านไฟล์ หรือ Object บางอย่างที่เราต้องการ Sync ระหว่างส่วนต่าง ๆ ของโปรแกรมเรา Concept นี้ เราอาจจะไม่เคยได้ยินบน Python เท่าไหร่ แต่ถ้าเขียนพวก C/C++ มา อาจจะเคยได้ยินมาก่อน มันใช้เยอะมาก ๆ ช่วยชีวิตเรามาเยอะละ มาเขียน Python เราเลยอยากพา Concept นี้มาใช้ด้วย แต่นางไม่มี เราก็ต้องเขียนเองไป

def singleton(cls):
    instances = {}
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class AnyClass:
    pass

ด้านในของ Singleton เราสร้าง instances เป็น Dict มาเพื่อเก็บ Object ที่เราสร้างเอาไป เพื่อให้ใน wrapper() ก่อนที่เราจะสร้าง Object เราจะเอาไปเช็คก่อนว่าใน instances มันมีอยู่แล้วมั้ย ถ้ามี ก็คืน Instance ที่เคยสร้างไว้ไป แต่ถ้าไม่มี มันก็จะสร้างอันใหม่ลงไป ในการใช้งาน เราก็แค่ยัด Decorator ลงไปบน Class ได้เลย

x = AnyClass()
y = AnyClass()

เรามาลองสร้าง Object จาก Class ที่เราใส่ Decorator Singleton เอาไว้ จาก Code ด้านบน ถ้าเราทำแบบนี้ปกติ เราน่าจะได้ Object 2 อันออกมาแต่เราใส่ Singleton มาแล้ว แปลว่า x และ y ควรจะเป็น Instance เดียวกัน เราลองมาเช็คกันดีกว่า

>>> x == y
True

เราลองเช็คดู เราจะเห็นได้เลยว่า x และ y เป็นตัวเดียวกันเลย ถ้าเราลอง Mutate x ตัว y ก็จะต้องเปลี่ยนตามด้วย เพราะมันคือตัวเดียวกัน นี่แหละคือความ Singleton อย่างง่ายใน Python

สรุป

3 Decorator ที่เราเอามาให้ดูกันในวันนี้จริง ๆ เป็นแค่ส่วนนึงของ Decorator ที่เราเขียนเอาไว้ใช้งานทั่ว ๆ ไป จริง ๆ มีอีกเป็นร้อยตัวเลย แต่เอาอันที่ใช้บ่อย ๆ ออกมาเล่า ถ้าใครอยากก๊อปไปใช้ก็ได้เลย มันไม่ได้เขียนยากอะไรมาก ถ้าใครมี Decorator เจ๋ง ๆ เอามาแชร์กันได้

Read Next...

จัดการข้อมูลบน Pandas ยังไงให้เร็ว 1000x ด้วย Vectorisation

จัดการข้อมูลบน Pandas ยังไงให้เร็ว 1000x ด้วย Vectorisation

เวลาเราทำงานกับข้อมูลอย่าง Pandas DataFrame หนึ่งในงานที่เราเขียนลงไปให้มันทำคือ การ Apply Function เข้าไป ถ้าข้อมูลมีขนาดเล็ก มันไม่มีปัญหาเท่าไหร่ แต่ถ้าข้อมูลของเราใหญ่ มันอีกเรื่องเลย ถ้าเราจะเขียนให้เร็วที่สุด เราจะทำได้โดยวิธีใดบ้าง วันนี้เรามาดูกัน...

ปั่นความเร็ว Python Script เกือบ 700 เท่าด้วย JIT บน Numba

ปั่นความเร็ว Python Script เกือบ 700 เท่าด้วย JIT บน Numba

Python เป็นภาษาที่เราใช้งานกันเยอะมาก ๆ เพราะความยืดหยุ่นของมัน แต่ปัญหาของมันก็เกิดจากข้อดีของมันนี่แหละ ทำให้เมื่อเราต้องการ Performance แต่ถ้าเราจะบอกว่า เราสามารถทำได้ดีทั้งคู่เลยละ จะเป็นยังไง เราขอแนะนำ Numba ที่ใช้งาน JIT บอกเลยว่า เร็วขึ้นแบบ 700 เท่าตอนที่ทดลองกันเลย...

Humanise the Number in Python with "Humanize"

Humanise the Number in Python with "Humanize"

หลายวันก่อน เราทำงานแล้วเราต้องการทำงานกับตัวเลขเพื่อให้มันอ่านได้ง่ายขึ้น จะมานั่งเขียนเองก็เสียเวลา เลยไปนั่งหา Library มาใช้ จนไปเจอ Humanize วันนี้เลยจะเอามาเล่าให้อ่านกันว่า มันทำอะไรได้ แล้วมันล่นเวลาการทำงานของเราได้ยังไง...

ทำไม 0.3 + 0.6 ถึงได้ 0.8999999 กับปัญหา Floating Point Approximation

ทำไม 0.3 + 0.6 ถึงได้ 0.8999999 กับปัญหา Floating Point Approximation

การทำงานกับตัวเลขทศนิยมบนคอมพิวเตอร์มันมีความลับซ่อนอยู่ เราอาจจะเคยเจอเคสที่ เอา 0.3 + 0.6 แล้วมันได้ 0.899 ซ้ำไปเรื่อย ๆ ไม่ได้ 0.9 เพราะคอมพิวเตอร์ไม่ได้มองระบบทศนิยมเหมือนกับคนนั่นเอง บางตัวมันไม่สามารถเก็บได้ เลยจำเป็นจะต้องประมาณเอา เราเลยเรียกว่า Floating Point Approximation...