Python函数装饰器

带参数的函数装饰器详解

from functools import wraps

# A decorator that takes arguments is a decorator factory:
def repeat(n=1):       # (A) factory: gets CONFIG at decoration time
                       # Called once at decoration time.
                       # Receives your configuration (n=3).
                       # Returns the decorator function deco, closing over n.
    print(f"[Decoration time - repeat] factory n={n}")

    def deco(func):        # (B) real decorator: gets FUNC at decoration time
                           # Immediately called by Python with the function being decorated (func = say_hello).
                           # Returns wrapper, which closes over both func and n.
                           # Desugared: say_hello = repeat(3)(say_hello)   <---- 重点
        print(f"[Decoration time - repeat] deco func={func.__name__}")

        @wraps(func)
        def wrapper(*args, **kwargs):  # (C) wrapper: gets CALL ARGS at call time
                                       # Called every time you call say_hello(...) at runtime.
                                       # Receives the original call’s positional/keyword args
            print(f"[repeat] wrapper args={args}, kwargs={kwargs}")
            result = None
            for i in range(n):
                print(f"  calling {func.__name__} #{i+1}")
                result = func(*args, **kwargs)
            return result
        return wrapper

    return deco

@repeat(n=3)       # ← runs (A) first; returns (B); then Python runs (B)(say_hello)
def say_hello(name):
    print(f"hello {name}")

if __name__ == "__main__":
    """
    There are two phases for decorators:
    1. Decoration time (when Python executes the @... line during function definition)
    2. Call time (when you later call the decorated function)
    流程:
    config → factory → decorator  → wrapper
    n      → repeat  → deco(func) → wrapper(*args, **kwargs)
    """
    print("-----------Main Starts--------------")
    say_hello("WORLD")

输出结果

[Decoration time - repeat] factory n=3
[Decoration time - repeat] deco func=say_hello
-----------Main Starts--------------
[repeat] wrapper args=('WORLD',), kwargs={}
  calling say_hello #1
hello WORLD
  calling say_hello #2
hello WORLD
  calling say_hello #3
hello WORLD
-----------------------------------
Calling do_something with (<__main__.LearnDecorator object at 0x00000210487B1940>, 1, 'one') and {'x': 2}
do_something ended with 10
do_something got return: 10

可选带参数的函数装饰器详解

在之前的基础上,我们稍微加大难度,让decorator的参数optional。

from functools import wraps
from typing import Callable, Optional

def log(_func: Optional[Callable] = None, *, prefix: str = "[LOG]"):
    """
    If used as @log:
        - When you write @log on a function, Python calls the decorator with the function as a single positional argument
        - _func = log(_func) # one positional arg: the function object; prefix uses default.

    If used as @log(prefix="X"):
        - _func is None, we return a decorator that knows prefix="X".

    参数列表为什么会出现 “*, prefix=”
    The * forces prefix to be keyword-only, preventing binding the function object to the wrong parameter.
    比如,第一种情况
    def bad_log(prefix='[LOG]', _func=None):  # prefix is positional
        ...
    @bad_log
    def f(x): ...
    这时,f = bad_log(f),f (the function) binds to prefix!

    第二种情况
    def log_no_star(_func=None, prefix='[LOG]'):
        ...
    @log_no_star("X")
    def g(y): ...
    这时,用户想要设X给prefix,但是"X" binds to _func, not prefix → TypeError
    """
    def deco(func: Callable):
        print(f"[Decoration time - log] deco func={func.__name__}, prefix={prefix}")

        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f"{prefix} calling {func.__name__} args={args} kwargs={kwargs}")
            return func(*args, **kwargs)
        return wrapper

    if _func is not None:
        # Case A: @log → desugars to  func = log(func)                     <---- 重点
        # Call deco immediately with the function and return the wrapper.
        return deco(_func)

    # Case B: @log(prefix="X") → desugars to func = log(prefix="X")(func)  <---- 重点
    # Return the decorator; Python will call it later with the function.
    return deco

@log
def a(x): return x

@log(prefix="[LOG-Prefix]")
def b(y): return y

if __name__ == "__main__":
    """
    There are two phases for decorators:
    1. Decoration time (when Python executes the @... line during function definition)
    2. Call time (when you later call the decorated function)
    """
    print("-----------Main Starts--------------")
    a(1)
    b(1)

输出结果

[Decoration time - log] deco func=a, prefix=[LOG]
[Decoration time - log] deco func=b, prefix=[LOG-Prefix]
-----------Main Starts--------------
[LOG] calling a args=(1,) kwargs={}
[LOG-Prefix] calling b args=(1,) kwargs={}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值