รวม 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 เจ๋ง ๆ เอามาแชร์กันได้