เดโคเรเตอร์ในไพธอน
บทความนี้อธิบายเกี่ยวกับเดโคเรเตอร์ในไพธอน
YouTube Video
เดโคเรเตอร์ในไพธอน
เดโคเรเตอร์ในไพธอนเป็นคุณสมบัติที่ทรงพลังซึ่งใช้เพื่อเพิ่มฟังก์ชันการทำงานเพิ่มเติมให้กับฟังก์ชันหรือเมธอด เดโคเรเตอร์ช่วยให้คุณเพิ่มฟังก์ชันใหม่โดยไม่ต้องแก้ไขโค้ดที่มีอยู่ ซึ่งทำให้โค้ดนำกลับมาใช้ซ้ำได้และดูแลรักษาได้ง่ายขึ้น
พื้นฐานของเดโคเรเตอร์
เดโคเรเตอร์ในไพธอนทำงานโดยการรับฟังก์ชันเป็นพารามิเตอร์และคืนค่าฟังก์ชันใหม่ที่มีฟังก์ชันการทำงานเพิ่มเติม ด้วยการใช้เดโคเรเตอร์ คุณสามารถเพิ่มการประมวลผลก่อนหรือหลังให้ฟังก์ชันได้อย่างง่ายดาย
หากไม่มีเดโคเรเตอร์ การเปลี่ยนแปลงพฤติกรรมของฟังก์ชันจำเป็นต้องแก้ไขโค้ดฟังก์ชันโดยตรง แต่ด้วยเดโคเรเตอร์ คุณสามารถขยายฟังก์ชันการทำงานโดยไม่ต้องเปลี่ยนการทำงานดั้งเดิม
โครงสร้างพื้นฐานของเดโคเรเตอร์
1def my_decorator(func):
2 def wrapper():
3 print("Before the function call")
4 func()
5 print("After the function call")
6 return wrapper
7
8@my_decorator
9def say_hello():
10 print("Hello!")
11
12say_hello()
ในตัวอย่างนี้ ฟังก์ชัน my_decorator
ทำหน้าที่เป็นเดโคเรเตอร์ที่ครอบฟังก์ชัน say_hello
เดโคเรเตอร์ถูกใช้ด้วยไวยากรณ์ @my_decorator
และเมื่อมีการเรียกใช้ say_hello()
การประมวลผลก่อนและหลังที่เดโคเรเตอร์กำหนดไว้จะถูกดำเนินการโดยอัตโนมัติ
เดโคเรเตอร์ทำงานอย่างไร
ตัวตกแต่งทำงานในขั้นตอนต่อไปนี้
-
เดโคเรเตอร์รับฟังก์ชัน (หรือเมธอด) เป็นพารามิเตอร์
-
มันกำหนดฟังก์ชันตัวห่อเพื่อรันฟังก์ชันดั้งเดิม
-
ฟังก์ชันตัวห่อทำการประมวลผลเพิ่มเติมก่อนหรือหลังจากรันฟังก์ชันดั้งเดิม
-
เดโคเรเตอร์จะคืนค่าฟังก์ชันตัวห่อ ผลลัพธ์คือ ฟังก์ชันดั้งเดิมถูกแทนที่ด้วยฟังก์ชันใหม่ที่ถูกตกแต่ง
เดโคเรเตอร์สำหรับฟังก์ชันที่มีอาร์กิวเมนต์
เมื่อใช้เดโคเรเตอร์กับฟังก์ชันที่มีอาร์กิวเมนต์ ฟังก์ชันตัวห่อต้องถูกปรับให้รับอาร์กิวเมนต์เหล่านั้น
1def my_decorator(func):
2 def wrapper(*args, **kwargs):
3 print("Before the function call")
4 result = func(*args, **kwargs)
5 print("After the function call")
6 return result
7 return wrapper
8
9@my_decorator
10def greet(name):
11 print(f"Hello, {name}!")
12
13greet("Alice")
โดยการใช้ *args
และ **kwargs
สามารถจัดการฟังก์ชันที่รับอาร์กิวเมนต์และคีย์เวิร์ดอาร์กิวเมนต์ได้ทุกจำนวน สิ่งนี้ทำให้เดโคเรเตอร์สามารถประยุกต์ใช้งานกับฟังก์ชันที่มีอาร์กิวเมนต์ทุกประเภทได้โดยทั่วไป
ตัวอย่างการใช้งานตัวตกแต่ง (Decorators)
ตัวตกแต่งสำหรับการบันทึก (Logging)
ตัวตกแต่งมักถูกใช้เพื่อเพิ่มฟังก์ชันการบันทึก ตัวอย่างเช่น การสร้างตัวตกแต่งที่บันทึกก่อนและหลังการเรียกใช้ฟังก์ชัน คุณสามารถบันทึกได้ว่าเมื่อใดที่ฟังก์ชันถูกเรียกใช้และใช้เวลานานเท่าใด
1import time
2
3def log_time(func):
4 def wrapper(*args, **kwargs):
5 start_time = time.time()
6 print(f"Calling function '{func.__name__}'...")
7 result = func(*args, **kwargs)
8 end_time = time.time()
9 print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10 return result
11 return wrapper
12
13@log_time
14def long_task(duration):
15 time.sleep(duration)
16 print("Task completed!")
17
18long_task(2)
ในตัวอย่างนี้ ตัวตกแต่ง log_time
จะวัดเวลาที่ใช้ในการทำงานของฟังก์ชันและแสดงผลเป็นบันทึก ฟังก์ชัน long_task
ถูกห่อด้วยตัวตกแต่ง และเวลาที่ใช้ในการทำงานจะถูกบันทึกในระหว่างรันไทม์
ตัวตกแต่งสำหรับการจัดการสิทธิ์
ตัวตกแต่งยังสามารถใช้เพื่อจัดการสิทธิ์ได้ ตัวอย่างเช่น คุณสามารถจำกัดการทำงานโดยตรวจสอบว่าผู้ใช้มีสิทธิ์ที่เฉพาะเจาะจงหรือไม่
1def requires_permission(user_role):
2 def decorator(func):
3 def wrapper(*args, **kwargs):
4 if user_role != "admin":
5 print("Permission denied!")
6 return
7 return func(*args, **kwargs)
8 return wrapper
9 return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13 print(f"User {user_id} has been deleted.")
14
15delete_user(123) # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456) # Permission denied!
ตัวตกแต่ง requires_permission
จะจำกัดการทำงานของฟังก์ชันตามบทบาทของผู้ใช้ โดยการรวมตรรกะดังกล่าวเข้าในตัวตกแต่ง คุณสามารถป้องกันไม่ให้ตรรกะการจัดการสิทธิ์กระจัดกระจายไปทั่วโค้ด และช่วยเพิ่มความอ่านง่าย
การซ้อนตัวตกแต่ง (Nesting Decorators)
สามารถใช้ตัวตกแต่งหลายตัวได้ เมื่อตัวตกแต่งหลายตัวถูกใช้กับฟังก์ชันเดียวกัน พวกมันจะทำงานตามลำดับจากบนลงล่าง
1def uppercase(func):
2 def wrapper(*args, **kwargs):
3 result = func(*args, **kwargs)
4 return result.upper()
5 return wrapper
6
7def exclaim(func):
8 def wrapper(*args, **kwargs):
9 result = func(*args, **kwargs)
10 return result + "!"
11 return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16 return f"Hello, {name}"
17
18print(greet("Alice")) # "HELLO, ALICE!"
ในตัวอย่างนี้ ตัวตกแต่งสองตัวถูกใช้กับฟังก์ชัน greet
ตัวตกแต่ง @exclaim
เพิ่มเครื่องหมายตกใจที่ท้ายสตริง และ @uppercase
แปลงสตริงให้เป็นตัวพิมพ์ใหญ่
ตัวตกแต่งสำหรับเมธอดในคลาส
ตัวตกแต่งสามารถใช้กับเมธอดในคลาสได้ สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณต้องการควบคุมพฤติกรรมของเมธอดในคลาส เมื่อใช้ตัวตกแต่งกับเมธอดในคลาส คุณจำเป็นต้องใส่ใจเกี่ยวกับอาร์กิวเมนต์เช่น self
หรือ cls
1def log_method_call(func):
2 def wrapper(self, *args, **kwargs):
3 print(f"Calling method '{func.__name__}'...")
4 return func(self, *args, **kwargs)
5 return wrapper
6
7class MyClass:
8 @log_method_call
9 def greet(self, name):
10 print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
ในตัวอย่างนี้ ตัวตกแต่ง log_method_call
ถูกใช้กับเมธอด greet
เพื่อแสดงบันทึกเมื่อเมธอดถูกเรียกใช้งาน
ข้อควรระวังเกี่ยวกับตัวตกแต่ง
มีสิ่งที่ต้องพิจารณาเมื่อใช้ตัวตกแต่ง ตัวตกแต่งสามารถเปลี่ยนชื่อของฟังก์ชันเดิมและสตริงเอกสาร (เช่น __name__
หรือ __doc__
) ดังนั้นแนะนำให้ใช้ functools.wraps
เพื่อรักษาข้อมูลเหล่านี้
1import functools
2
3def my_decorator(func):
4 @functools.wraps(func)
5 def wrapper(*args, **kwargs):
6 print("Before the function call")
7 return func(*args, **kwargs)
8 return wrapper
การใช้ @functools.wraps
ช่วยให้ข้อมูลเมตาของฟังก์ชันเดิมถูกส่งต่อไปยังฟังก์ชันที่ห่อไว้อย่างถูกต้อง
สรุป
ตัวตกแต่ง (Decorators) ใน Python เป็นเครื่องมือที่ทรงพลังอย่างมากที่ช่วยให้คุณสามารถเพิ่มฟังก์ชันเพิ่มเติมให้กับฟังก์ชันและเมธอดได้อย่างกระชับ สามารถใช้ตัวตกแต่งในหลากหลายสถานการณ์เพื่อลดการซ้ำซ้อนของโค้ด และปรับปรุงการบำรุงรักษาและการนำกลับมาใช้ใหม่ เมื่อคุณเข้าใจวิธีการทำงานของตัวตกแต่ง คุณจะสามารถเขียนโค้ดที่มีประสิทธิภาพและยืดหยุ่นมากขึ้น
คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย