เดโคเรเตอร์ในไพธอน

เดโคเรเตอร์ในไพธอน

บทความนี้อธิบายเกี่ยวกับเดโคเรเตอร์ในไพธอน

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() การประมวลผลก่อนและหลังที่เดโคเรเตอร์กำหนดไว้จะถูกดำเนินการโดยอัตโนมัติ

เดโคเรเตอร์ทำงานอย่างไร

ตัวตกแต่งทำงานในขั้นตอนต่อไปนี้

  1. เดโคเรเตอร์รับฟังก์ชัน (หรือเมธอด) เป็นพารามิเตอร์

  2. มันกำหนดฟังก์ชันตัวห่อเพื่อรันฟังก์ชันดั้งเดิม

  3. ฟังก์ชันตัวห่อทำการประมวลผลเพิ่มเติมก่อนหรือหลังจากรันฟังก์ชันดั้งเดิม

  4. เดโคเรเตอร์จะคืนค่าฟังก์ชันตัวห่อ ผลลัพธ์คือ ฟังก์ชันดั้งเดิมถูกแทนที่ด้วยฟังก์ชันใหม่ที่ถูกตกแต่ง

เดโคเรเตอร์สำหรับฟังก์ชันที่มีอาร์กิวเมนต์

เมื่อใช้เดโคเรเตอร์กับฟังก์ชันที่มีอาร์กิวเมนต์ ฟังก์ชันตัวห่อต้องถูกปรับให้รับอาร์กิวเมนต์เหล่านั้น

 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 ด้วย

YouTube Video