...

Python实现同时支持带调用和不调用带装饰器

一般来说,不带参数装饰器,再使用时不加括号,带参数的装饰器使用时必须加括号。例如

  • 不带参装饰器

def info(func):  # 装饰器,参数接收一个函数,返回一个函数
    @functools.wraps(func)
    def wrapper(*args, **kwargs):   # 与原函数功能一致,支持任意参数
        print(f'调用函数: {func.__name__} 参数:{args} {kwargs}')
        return func(*args, **kwargs)   # 内部包裹调用原函数

    return wrapper  # 返回和原函数功能一致的函数

@info   # 使用时不加括号
def add(a, b):
    return a + b
  • 带参数的装饰器(返回装饰器的函数)
def info(show_result=True):

    def _info(func):  # 装饰器,参数接收一个函数,返回一个函数
        @functools.wraps(func)
        def wrapper(*args, **kwargs):  # 与原函数功能一致,支持任意参数
            print(f'调用函数: {func.__name__} 参数:{args} {kwargs}')
            start_time = time.time()
            result = func(*args, **kwargs)  # 内部包裹调用原函数
            if show_result is True:
                print(f'调用结果: {result} 耗时: {time.time()-start_time}')
            return result

        return wrapper  # 返回替换后的新函数

    return _info

@info()  # 即使使用默认参数也必须加括号调用(因为调用后才能得到装饰器)
def add(a, b):
    return a + b

注意: 带参数装饰器虽然通过参数支持定制,但是使用时必须加括号,用户很容易遗漏或分不清是否需要加括号

能不能像@pytest.fixture那样,既可以不带参数,也可以带参数使用呢,例如:

import pytest

@pytest.fixture
def a(): ...

@pytest.fixture()
def b(): ...

@pytest.fixture(scope='module')
def c(): ...

这就要求,我们装饰器外部函数,即可以本身作为一个装饰器(接收一个原函数,返回一个同功能函数),也可以根据参数调用后返回一个装饰器,实现如下:

def info(func=None, show_result=False):  # 第一个参数为函数

    def _info(func):  # 装饰器,参数接收一个函数,返回一个函数
        @functools.wraps(func)
        def wrapper(*args, **kwargs):  # 与原函数功能一致,支持任意参数
            print(f'调用函数: {func.__name__} 参数:{args} {kwargs}')
            start_time = time.time()
            result = func(*args, **kwargs)  # 内部包裹调用原函数
            if show_result is True:
                print(f'调用结果: {result} 耗时: {time.time()-start_time}')
            return result

        return wrapper  # 返回替换后的新函数

    if func is not None:  # 不带括号(作为装饰器)使用时
        return _info(func)  # 返回 同功能函数wrapper(即_info的调用结果)
    return _info  # 否则,带括号调用(作为返回装饰器的函数)使用时,返回装饰器 _info



@info  # 即使使用默认参数也必须加括号调用(因为调用后才能得到装饰器)
def add(a, b):
    return a + b

@info()
def sub(a, b):
    return a - b

@info(show_result=True)
def mul(a, b):
    return a * b

注意:1. 在使用装饰器info()不能手动指定第一个参数func=...
2. 在指定参数时,只能按key=value形式给出

posted @ 2023-06-13 11:11  韩志超  阅读(48)  评论(0编辑  收藏  举报