|

楼主 |
发表于 2020-1-15 01:02:42
|
显示全部楼层
可自定义属性的装饰器
本帖最后由 Stubborn 于 2020-1-16 20:37 编辑
05节 -_- 可自定义属性的装饰器
在这之前,稍微介绍`functools.partial` 用于修改,固定,添加函数的参数,返回一个新的函数。下面看简单的例子。
- # -*- coding: utf-8 -*-
- # !/usr/bin/python3
- """
- @ Version : ??
- @ Author : Alex
- @ Software : Pycharm
- @ Time : 2020/1/2 下午2:20
- """
- from functools import partial
- from inspect import signature
- def test(a , b, c=5):
- return a * b * c
- print(signature(test)) # -> (a, b, c=5)
- test_one = partial(test, 2, c=4) # new func
- print(signature(test_one)) #(b, *, c=4)
- print(test_one(5)) # return
- def test_two(*args, **kwargs):
- return sum(args) + sum(list(kwargs.values()))
- print(signature(test_two))
- test_three = partial(test_two, 2, 3, d=5, c=4)
- print(signature(test_three))
- print(test_three(6))
复制代码
问题:我们想编写一个装饰器来包装函数,但是可以让用户调整装饰器的属性,这样在运行时能够控制装饰器的行为。
解答:引入了访问器函数(accessor function),通过使用nonlocal关键字声明变量来修改装饰器内部的属性。之后把访问器函数作为函数属性附加到包装函数上。
代码如下
- # -*- coding: utf-8 -*-
- # !/usr/bin/python3
- """
- @ Version : ??
- @ Author : Alex
- @ Software : Pycharm
- @ Time : 2020/1/2 下午2:20
- 我们想编写一个装饰器来包装函数,但是可以让用户调整装饰器的属性,这样在运行时能够控制装饰器的行为。
- 引入了访问器函数(accessor function),通过使用nonlocal关键字声明变量来修改装饰器内部的属性。
- 之后把访问器函数作为函数属性附加到包装函数上。
- """
- from functools import wraps, partial
- import logging
- def attach_wrapper(func_obj, func=None):
- """将函数附加为obj的属性"""
- if func is None:
- return partial(attach_wrapper, func_obj)
- setattr(func_obj, func.__name__, func)
- return func
- def logged(level, name=None, message=None):
- """ 日志记录器
- :param level:日志记录级别
- :param name:记录器名称
- :param message:是日志消息
- # 如果未指定名称和消息,它们默认为函数的模块和名称。
- """
- def decorate(func):
- logname = name if name else func.__module__
- logmsg = message if message else func.__name__
- @wraps(func)
- def wrapper(*args, **kwargs):
- print(f"[level:{level}]\tname:{logname}\tmessage:{logmsg}")
- return func(*args, **kwargs)
- # Attach setter functions
- @attach_wrapper(wrapper)
- def set_level(newlevel):
- nonlocal level
- level = newlevel
- @attach_wrapper(wrapper)
- def set_message(newmsg):
- nonlocal logmsg
- logmsg = newmsg
- return wrapper
- return decorate
- # Example use
- @logged(logging.DEBUG)
- def add(x, y):
- return x + y
- def spam(x, y):
- return x + y
- # -----一下是功能演示-------------------
- print("---Example use fun__add__")
- add(3, 2)
- add.set_message("Alex")
- add(3, 2)
- # 等价调用
- print("---Example use fun__spam__")
- spam = logged(level=25)(spam) # spam -> wrapper 包含闭包内的局部变量,以及方法
- spam(3, 2)
- spam.set_message("Alex is good girl") # 通过 nonlocal 修改局部变量
- spam(3, 2)
复制代码
能打到目的(调整装饰器的属性),示例中的关键,就是访问器函数(set_message()和set_level())。它们以属性的形式附加到了包装函数上。每个访问器函数允许对nonlocal变量赋值来调整内部参数。而且访问器函数可以跨越多个装饰器层进行传播(如果所有的装饰器都使用了@functools.wraps 的话)
- def time_this(function):
- @wraps(function)
- def wrapper(*args, **kwargs):
- start = time.time()
- function(*args, **kwargs)
- print(function.__name__, start - time.time())
- return wrapper
- @time_this
- @logged(logging.DEBUG)
- def countdown(n):
- while n > 0:
- n -= 1
复制代码
末尾补充:关于上面访问器函数和装饰器,如果看不明白,可以参考下面的代码和第一个介绍partial的代码
- def attach_wrapper(func_obj, func=None):
- if func is None:
- return partial(attach_wrapper, func_obj)
- setattr(func_obj, func.__name__, func)
- return func
- def function():
- pass
- @attach_wrapper(function)
- def test(name):
- print(name)
- function.test(123)
复制代码
|
|