本帖最后由 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)
|