Decorators
Decorators: การเพิ่มพฤติกรรมโดยไม่แก้ไขโค้ดเดิม
หัวข้อที่มีชื่อว่า “Decorators: การเพิ่มพฤติกรรมโดยไม่แก้ไขโค้ดเดิม”TypeScript ก็มี decorator เหมือนกัน แต่ยังเป็นฟีเจอร์ทดลอง ผูกอยู่กับ class และพฤติกรรมยังเปลี่ยนไปมาตาม TypeScript กับ ECMAScript proposal
ส่วน decorator ของ Python นิ่งกว่า เรียบง่ายกว่า และใช้ได้กับ function ทุกแบบ ไม่จำกัดแค่ class method framework ฝั่ง Python ใช้กันหนักมาก ทั้ง Flask route, FastAPI endpoint, Celery task และ dataclasses.dataclass จนแทบทุก codebase ต้องมีให้เห็น
Decorator คือ function ที่รับ function มาและคืน function ใหม่กลับไป syntax @name เป็นเพียง shorthand ของ func = name(func)
การ wrap function
หัวข้อที่มีชื่อว่า “การ wrap function”รูปแบบหลักคือการ wrap function ที่มีอยู่เพื่อเพิ่มพฤติกรรมก่อนและหลังการเรียก ฝั่ง TypeScript หากต้องการผลเดียวกันกับ plain function จะต้องใช้ higher-order function ที่เรียกด้วยตนเอง หรือ class decorator ที่ใช้ได้กับ method เท่านั้น
// TypeScript: class method decorator (experimental, stage 3)// Works on class methods, not plain functionsfunction log(target: any, key: string, descriptor: PropertyDescriptor) { const original = descriptor.value; descriptor.value = function (...args: any[]) { console.log(`Calling ${key}...`); const result = original.apply(this, args); console.log(`${key} finished.`); return result; }; return descriptor;}
class Greeter { @log greet(name: string) { console.log(`Hello, ${name}!`); }}
// For a plain function you must wrap it manually:function withLog<T extends (...args: any[]) => any>(fn: T): T { return ((...args: any[]) => { console.log(`Calling ${fn.name}...`); const result = fn(...args); console.log(`${fn.name} finished.`); return result; }) as T;}
const greet = withLog((name: string) => console.log(`Hello, ${name}!`));greet("Alice");# Python: decorators work on any function, syntax is @nameimport functools
def timer_decorator(func): @functools.wraps(func) # preserves __name__, __doc__, etc. def wrapper(*args, **kwargs): print(f"Calling {func.__name__}...") result = func(*args, **kwargs) print(f"{func.__name__} finished.") return result return wrapper
@timer_decorator # equivalent to: greet = timer_decorator(greet)def greet(name): print(f"Hello, {name}!")
greet("Alice")# Output:# Calling greet...# Hello, Alice!# greet finished.Decorator ที่รับ argument: decorator factory
หัวข้อที่มีชื่อว่า “Decorator ที่รับ argument: decorator factory”เมื่อคุณต้องการส่ง configuration ให้ decorator คุณเพิ่ม outer function ที่รับ argument และคืน decorator จริงกลับไป รูปแบบนี้เรียกว่า decorator factory
def repeat(n): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator
@repeat(3) # repeat(3) returns decorator; decorator(say_hi) returns wrapperdef say_hi(): print("Hi!")
say_hi() # prints "Hi!" three timesการซ้อนสามชั้น (factory -> decorator -> wrapper) คือรูปแบบมาตรฐานเมื่อ decorator ต้องการ parameter
ลองทดสอบ
หัวข้อที่มีชื่อว่า “ลองทดสอบ”import functools
def timer_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f"Calling {func.__name__}...") result = func(*args, **kwargs) print(f"{func.__name__} finished.") return result return wrapper
@timer_decoratordef greet(name): print(f"Hello, {name}!")
greet("Alice")
def repeat(n): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator
@repeat(3)def say_hi(): print("Hi!")
say_hi()Loading Python runtime (first run only)…