2.23. Decorators¶
decorator คือไวยากรณ์ที่ห่อฟังก์ชัน (หรือเมธอด) ไว้ในฟังก์ชันอื่น wrapper สามารถเพิ่มพฤติกรรมก่อนและหลังการเรียกเดิม แทนที่ค่าที่คืนมา หรือแนบ metadata รูปแบบคือ:
@wrapper
def f():
...
บรรทัด @wrapper เทียบเท่ากับ f = wrapper(f) -- ฟังก์ชัน f ถูกสร้างตามปกติ จากนั้นส่งต่อให้ wrapper และผลลัพธ์ถูกผูกกลับคืนให้กับชื่อ f
decorator รับฟังก์ชันเข้าและคืนฟังก์ชันใหม่ออกมา¶
2.23.1. Method decorators ในตัว¶
Python มาพร้อม decorator บางตัวที่ใช้ภายในเนื้อหาของคลาส
2.23.1.1. @property¶
เปลี่ยนเมธอดให้กลายเป็น computed attribute ผู้เรียกเข้าถึงมันราวกับว่าเป็น attribute ธรรมดา (ไม่มีวงเล็บ) แต่เมธอดจะทำงานในแต่ละการอ่าน:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius * self.radius
c = Circle(5)
print(c.area) # 78.53975 -- no parentheses
ใช้เมื่อ attribute ที่ ดู เรียบง่ายต้องการการคำนวณเล็กน้อยอยู่เบื้องหลัง หากงานมีค่าใช้จ่ายสูง ควรใช้เมธอดธรรมดาแทน -- ผู้เรียกไม่คาดหวังว่าการอ่าน attribute จะช้า
2.23.1.2. @classmethod¶
กำหนดเมธอดที่รับ คลาส เป็นอาร์กิวเมนต์แรกแทนที่จะเป็น instance พารามิเตอร์แรกตามธรรมเนียมจะชื่อ cls:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
p = Point.origin()
Class method คือวิธีมาตรฐานในการให้ alternative constructors -- ฟังก์ชัน factory ที่คืนค่า instance ที่สร้างในแบบที่ไม่ใช่ค่าเริ่มต้น
2.23.1.3. @staticmethod¶
กำหนดเมธอดที่ไม่รับทั้ง instance และคลาส -- มันเป็นเพียงฟังก์ชันธรรมดาที่อยู่ใน namespace ของคลาสเพื่อจุดประสงค์ด้านการจัดระเบียบ:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
ใช้อย่างประหยัด ถ้าฟังก์ชันไม่มีความเกี่ยวข้องกับสถานะของคลาสจริงๆ ฟังก์ชันระดับโมดูลธรรมดามักจะสะอาดกว่า
2.23.2. การเขียน decorator กำหนดเอง¶
decorator คือฟังก์ชันที่รับฟังก์ชันและคืนฟังก์ชัน รูปแบบขั้นต่ำ:
def log_calls(func):
def wrapper(*args, **kwargs):
print("calling", func.__name__)
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
ผลลัพธ์:
calling add
wrapper ปิดทับ func และส่งต่อทุกอย่างไปยังมัน *args / **kwargs ทำให้มันทำงานได้กับฟังก์ชันใดก็ตาม ไม่ใช่แค่ฟังก์ชันสองอาร์กิวเมนต์ รูปแบบนี้เป็นพื้นฐานของ decorator ที่ซับซ้อนมากขึ้น (การจับเวลา, การแคช, การลองใหม่เมื่อล้มเหลว) แต่แก่นกลางเสมอเหมือนกัน: รับฟังก์ชันเข้า คืนฟังก์ชันออก