Python 参数化装饰器 1.实现注册注销功能 2.可配置打印格式的函数计时器

想要实现装饰器接收参数,需要创建一个装饰器工厂函数,把参数传给他,返回一个装饰器,然后再把它用在要装饰去函数上。

 

一个参数化的装饰器,让register装饰器具备可选注册和注销功能。通过设置一个参数,设为FALSE时,不注册被装饰去函数。

注意此时register函数不是装饰器,而是装饰器工厂函数。调用它时才会返回真正的装饰器。

 

示例,接收参数的注册功能装饰器

registry = set()  # 使用集合,添加和删除速度更快


def register(active=True):
    def decorate(func):  # decorate才是装饰器
        if active:  # 通过闭包获取active的值,是True时才注册
            registry.add(func)
        else:
            registry.discard(func)
        return func

    return decorate


@register(active=False)
def f1():
    print('f1 running.')


@register()
def f2():
    print('f2 running.')


def f3():
    print('f3 running.')


print('main running')
print('registry ->', registry)
打印
main running
registry -> {}

扩展:

使用set集合比list添加和删除的速度更快

set.discard(x) 删除指定元素,set. remove() 方法在移除一个不存在的元素时会发生错误,而 discard() 方法不会。

 

以上可以看到只有f2函数被注册了,f1虽然被装饰了,但是active=False,所以f1不会被注册。

如果不使用@语法,像常规函数那样使用register,装饰f2函数就是register()(f2),装饰f1函数就是register(active=False)(f1)

 

一个完整的三层函数的装饰器

最外层的函数是装饰器的工厂函数。第二层才是真正的装饰器,第三层是用来包装被装饰的函数用的。

 

 

示例,一个clock装饰器,参数是格式字符串。

"""clockdeco_param.py"""
import time
import functools

FMT = '[{time_use:0.8f}s] {func_name}({parameter}) -> {result}'  # 格式化的模板 (作为装饰器参数)


def clock(fmt=FMT):
    """函数执行时间,参数,结果"""

    def decorate(func):
        @functools.wraps(func)
        def inner(*args, **kwargs):
            t0 = time.time()
            result = func(*args, **kwargs)
            time_use = time.time() - t0
            func_name = func.__name__
            parameter = []
            if args:
                parameter.append(','.join(repr(arg) for arg in args))
            if kwargs:
                kwargs_str = ",".join('%s=%r' % (k, v) for k, v in kwargs.items())
                parameter.append(kwargs_str)
            parameter = ','.join(parameter)
            # print('[%0.8fs] %s(%s) -> %r' % (time_use, func_name, parameter, result))
            print(fmt.format(**locals()))  # 这里拆包局部变量,省去了参数字典
            return result

        return inner

    return decorate


@clock()
def test(s, a=2):
    time.sleep(1)
    print('func test running')
    return 'over'


test('wo', a=3)
打印
func test running
[1.01234961s] test('wo',a=3) -> over

clock是参数化装饰器的工厂函数,decorate才是真正的装饰器。

 

扩展:

代码中使用了locals()然后拆包,locals()里面保存了inner函数的局部变量信息,是个字典,如果打印出来就是:

{'args': ('wo',), 'kwargs': {'a': 3}, 't0': 1618408087.1784744, 'result': 'over', 'time_use': 1.0123496055603027, 'func_name': 'test', 'parameter': "'wo',a=3", 'kwargs_str': 'a=3', 'fmt': '[{time_use:0.8f}s] {func_name}({parameter}) -> {result}', 'func': }

 

如果在其他文件中使用clockdeco_param.py模块中的clock装饰器,可以:

from clockdeco_param import clock

 

@clock('{func_name}: {time_use}s')

def func():pass

 

这样就可以随意改变输出格式:

func: 0s

 

你可能感兴趣的:(流畅的Python读书笔记,python)