0Pricing
Python Academy · บทเรียน

การเขียนเดคอเรเตอร์แบบกำหนดเอง

สร้างเดคอเรเตอร์ด้วยฟังก์ชันตัวห่อและ functools.wraps

การเขียนเดคอเรเตอร์แบบกำหนดเอง เป็นบทเรียน Python Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Python Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

บทนำ

เดคอเรเตอร์คือฟังก์ชันที่ครอบฟังก์ชันอื่นเพื่อเพิ่มพฤติกรรมก่อนหรือหลังการทำงาน โดยไม่แก้ไขฟังก์ชันเดิม

เดคอเรเตอร์คืออะไร

การวาง @decorator ไว้เหนือฟังก์ชันเป็นรูปแบบย่อทางไวยากรณ์ของ: func = decorator(func) เดคอเรเตอร์จะรับฟังก์ชันเข้ามาและส่งคืน wrapper
def shout(func):
    def wrapper(*a, **kw):
        result = func(*a, **kw)
        return str(result).upper()
    return wrapper
@shout
def greet(name): return f'hello {name}'
print(greet('Alice'))

รูปแบบ Wrapper

ฟังก์ชัน wrapper มีซิกเนเจอร์เดียวกับฟังก์ชันเดิม โดยเรียกฟังก์ชันเดิมจากภายในและเพิ่มพฤติกรรมล้อมรอบการทำงานนั้น
def timer(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f'{func.__name__} took {time.time()-start:.4f}s')
        return result
    return wrapper
@timer
def slow(): import time; time.sleep(0.01)
slow()

functools.wraps

การใช้ @wraps(func) กับ wrapper จะรักษา __name__, __doc__ และ __annotations__ ของฟังก์ชันเดิมไว้
from functools import wraps
def log(func):
    @wraps(func)
    def wrapper(*a, **kw):
        print(f'calling {func.__name__}')
        return func(*a, **kw)
    return wrapper
@log
def add(a, b): return a + b
print(add.__name__)
add(1, 2)

เดคอเรเตอร์ที่รับอาร์กิวเมนต์

เดคอเรเตอร์ที่รับอาร์กิวเมนต์ต้องมีการครอบเพิ่มอีกหนึ่งระดับ นั่นคือฟังก์ชันโรงงานที่รับอาร์กิวเมนต์แล้วส่งคืนเดคอเรเตอร์
def repeat(n):
    def decorator(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*a, **kw):
            for _ in range(n): func(*a, **kw)
        return wrapper
    return decorator
@repeat(3)
def hi(): print('hi')
hi()

การซ้อนเดคอเรเตอร์

เดคอเรเตอร์หลายตัวจะซ้อนกันจากล่างขึ้นบน: @d2 @d1 หมายถึง func = d2(d1(func)) เดคอเรเตอร์ชั้นนอกสุดหรือด้านบนจะทำงานเป็นลำดับสุดท้าย
def bold(f):
    from functools import wraps
    @wraps(f)
    def w(*a,**k): return '<b>' + f(*a,**k) + '</b>'
    return w
def italic(f):
    from functools import wraps
    @wraps(f)
    def w(*a,**k): return '<i>' + f(*a,**k) + '</i>'
    return w
@bold
@italic
def text(): return 'hello'
print(text())

เดคอเรเตอร์ที่สร้างจากคลาส

คลาสที่มี __call__ สามารถทำหน้าที่เป็นเดคอเรเตอร์ได้ และสามารถเก็บสถานะระหว่างการเรียกแต่ละครั้ง
class Counter:
    def __init__(self, func): self.func,self.count=func,0
    def __call__(self, *a, **kw):
        self.count+=1
        return self.func(*a, **kw)
@Counter
def greet(name): return f'hi {name}'
greet('Alice'); greet('Bob')
print(greet.count)

เดคอเรเตอร์สำหรับจดจำผลลัพธ์

เก็บผลลัพธ์ก่อนหน้าไว้เพื่อหลีกเลี่ยงการคำนวณซ้ำ: if args in cache: return cache[args] functools.lru_cache มีความสามารถนี้มาให้ในตัว
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)
print(fib(30))

เดคอเรเตอร์สำหรับการอนุญาต

เดคอเรเตอร์มักใช้ควบคุมการเข้าถึง เช่น ตรวจสอบว่าผู้ใช้เข้าสู่ระบบแล้วหรือไม่ก่อนเรียกใช้ฟังก์ชัน
def requires_auth(func):
    from functools import wraps
    @wraps(func)
    def wrapper(user, *a, **kw):
        if not user.get('authenticated'):
            raise PermissionError('Not authenticated')
        return func(user, *a, **kw)
    return wrapper
@requires_auth
def dashboard(user): return 'Welcome!'
try: dashboard({'authenticated': False})
except PermissionError as e: print(e)

เดคอเรเตอร์สำหรับลองใหม่

ครอบฟังก์ชันเพื่อให้ลองใหม่เมื่อทำงานล้มเหลว: for _ in range(retries): try: return func() except Exception: pass
import random
from functools import wraps
def retry(times=3):
    def dec(func):
        @wraps(func)
        def wrapper(*a, **kw):
            for i in range(times):
                try: return func(*a, **kw)
                except Exception as e:
                    if i==times-1: raise
        return wrapper
    return dec
@retry(3)
def flaky():
    if random.random()<0.7: raise ValueError
    return 'ok'
try: print(flaky())
except ValueError: print('failed')

แนวทางปฏิบัติที่ดีสำหรับเดคอเรเตอร์

ใช้ @functools.wraps เสมอ ให้เดคอเรเตอร์แต่ละตัวรับผิดชอบเพียงหน้าที่เดียว อธิบายสิ่งที่เดคอเรเตอร์เพิ่มเข้ามา และทดสอบเดคอเรเตอร์แยกจากฟังก์ชันที่ถูกตกแต่ง
from functools import wraps
def validate_positive(func):
    @wraps(func)
    def wrapper(x, *a, **kw):
        if x <= 0: raise ValueError(f'{x} must be positive')
        return func(x, *a, **kw)
    return wrapper
@validate_positive
def sqrt(x): return x**0.5
print(sqrt(9))

ตรวจสอบอย่างรวดเร็ว

เมื่อใช้ @functools.wraps(func) กับ wrapper จะเกิดอะไรขึ้น

ทบทวน

เดคอเรเตอร์: func = decorator(func) ใช้ wrapper(*args, **kwargs) โดย @wraps จะรักษาเมทาดาทาไว้ เดคอเรเตอร์แบบกำหนดพารามิเตอร์จะเพิ่มการครอบอีกหนึ่งระดับ และสามารถซ้อนกันด้วย @decorators หลายตัว

เรียนรู้ต่อไป

คุณมีความก้าวหน้าอย่างยอดเยี่ยม โปรดเรียนรู้ต่อไปเพื่อทำความเข้าใจแนวคิดถัดไปอย่างเชี่ยวชาญ

คำถามที่พบบ่อย

บทเรียน “การเขียนเดคอเรเตอร์แบบกำหนดเอง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนเดคอเรเตอร์แบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Python Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนเดคอเรเตอร์แบบกำหนดเอง”

สร้างเดคอเรเตอร์ด้วยฟังก์ชันตัวห่อและ functools.wraps คุณปฏิบัติ Python Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Python Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Python Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนเดคอเรเตอร์แบบกำหนดเอง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Python Academy นี้ได้ไหม

ได้ บทเรียน Python Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ฟังก์ชันในฐานะออบเจ็กต์ชั้นหนึ่ง
  2. การเขียนเดคอเรเตอร์แบบกำหนดเอง
  3. เดคอเรเตอร์ @property
  4. @classmethod และ @staticmethod
← กลับไปที่ Python Academy