Python 之 装饰器及文档字符串 随性笔记
1、装饰器
- 装饰器(Decorators)是 Python 的一个重要部分。简单地说:它们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)
- 装饰器分为 无参装饰器 和 有参装饰器
- 装饰器是一个函数,函数作为它的形参
- 无参装饰器实际上就是单形参函数
- 有参装饰器实际上就是多形参函数
- 可以使用
@functionname
或@functionname(参数列表)
方式,简化调用 - 装饰器可以是高阶函数,但装饰器是对传入函数的功能的装是(功能增强)
2、文档字符串
- Python 文档字符串 Documentation Strings
- 在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
- 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
- 可以使用特殊属性
__doc__
访问这个文档
def add(x, y):
"""
This is a function of addition.
"""
return x + y
print("name = {}\ndoc = {}".format(add.__name__, add.__doc__, add.__defaults__))
print('=' * 55)
print(help(add))
name = add
doc =
This is a function of addition.
=======================================================
Help on function add in module __main__:
add(x, y)
This is a function of addition.
None
3、装饰器执行过程
3.1 请浏览以下代码
-
装饰器 代码
import datetime, functools def logger(fn): print('=== logger start ===') print('{} fn_id is {}'.format(fn.__name__, id(fn))) @functools.wraps(fn) def wrapper(*args, **kwargs): print('=== wrapper start ===') print('{} fn_id is {}'.format(fn.__name__, id(fn))) start = datetime.datetime.now() ret = fn(*args, **kwargs) delta = (datetime.datetime.now() - start).total_seconds() if delta > 3: print('Too slow.') print('=== wrapper end ===') return ret print('=== logger end ===') return wrapper
-
其它函数代码,注意在 被装饰函数 没有被调用的时候,即在被装饰 函数定义的时候,装饰器已经执行过了,注意
print
语句# add1(args) => logger(add1)(args) => functools.wraps(add1)(wrapper)(args) @logger def add1(x, y): time.sleep(2) return x + y print('=' * 25) @logger # add2 => logger(add2) def add2(x, y, z): time.sleep(4) return x + y + z print('=' * 25) print(add1.__name__, add2.__name__)
# 上述代码执行结果 === logger start === add1 fn_id is 121600328 === logger end === ========================= === logger start === add2 fn_id is 121601048 === logger end === ========================= add1 add2
-
执行 被装饰函数
print('add1 >>>', add1(1, 2))
print('add2 >>>', add2(1, 2, 3))
# 上述代码执行结果
=== wrapper start ===
add1 fn_id is 121600328
=== wrapper end ===
add1 3
=== wrapper start ===
add2 fn_id is 121601048
Too slow.
=== wrapper end ===
add2 6
3.2 结合以下问题进行思考
-
logger
什么时候执行定义 被装饰函数 的时候执行
-
logger
执行过几次装饰几个函数,执行几次
-
wraps
装饰器什么时候执行执行 被装饰函数 的时候
-
wraps
装饰器执行过几次每执行一个 被装饰函数,执行一次
-
wrapper
的__name__
等属性被覆盖过几次没有覆盖,每次调用都不是同一个函数对象
4、装饰器函数的版本迭代改进
4.1 第一版,函数属性会被替换
import datetime
import time
def logger(fn):
def wrapper(*args, **kwargs):
"""I am wrapper."""
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
duration = datetime.datetime.now() - start
print('function {} took {}s.'.format(fn.__name__, duration.total_seconds()))
return ret
return wrapper
@logger # 相当于 add = logger(add)
def add(x,y):
"""I am add."""
time.sleep(2)
return x + y
print(add(5, y = 8))
print(add.__name__, add.__doc__)
function add took 2.0s.
13
wrapper I am wrapper.
4.2 第二版,构造函数还原函数属性
import datetime
import time
def copy_properties(src, dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args, **kwargs):
"""I am wrapper."""
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
duration = datetime.datetime.now() - start
print('function {} took {}s.'.format(fn.__name__, duration.total_seconds()))
copy_properties(fn, wrapper)
return ret
return wrapper
@logger # 相当于 add = logger(add)
def add(x,y):
"""I am add."""
time.sleep(2)
return x + y
print(add(5, y = 8))
print(add.__name__, '|||', add.__doc__)
function add took 2.0s.
13
add ||| I am add.
4.3 第三版,柯里化新建函数
import datetime
import time
# 柯里化
def copy_properties(src):
def _copy_properties(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy_properties
def logger(fn):
def wrapper(*args, **kwargs):
"""I am wrapper."""
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
duration = datetime.datetime.now() - start
print('function {} took {}s.'.format(fn.__name__, duration.total_seconds()))
# copy_properties(fn)(wrapper) => _copy_properties(wrapper)
copy_properties(fn)(wrapper)
return ret
return wrapper
@logger # 相当于 add = logger(add)
def add(x,y):
"""I am add."""
time.sleep(2)
return x + y
print(add(5, y = 8))
print(add.__name__, '|||', add.__doc__)
function add took 2.0s.
13
add ||| I am add.
4.4 第四版,改造成装饰器
import datetime
import time
def copy_properties(src):
def _copy_properties(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy_properties
def logger(fn):
@copy_properties(fn)
# 带参装饰器
# wrapper => copy_properties(fn)(wrapper) => _copy_properties(wrapper)
def wrapper(*args, **kwargs):
"""I am wrapper."""
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
duration = datetime.datetime.now() - start
print('function {} took {}s.'.format(fn.__name__, duration.total_seconds()))
# copy_properties(fn)( wrapper)
return ret
return wrapper
@logger
# 相当于 add = logger(add) => add = wrapper
# 无参装饰器,本质上等效为 单参数 的函数
def add(x,y): # lambda x, y:x + y
"""I am add."""
print('===== call add =====')
time.sleep(2)
return x + y
print(add(5, y = 8))
print(add.__name__, '|||', add.__doc__)
===== call add =====
function add took 2.0s.
13
add ||| I am add.
4.5 第五版,使用 functools
from functools import update_wrapper, wraps
import datetime
import time
def logger(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
"""I am wrapper."""
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
duration = datetime.datetime.now() - start
print('function {} took {}s.'.format(fn.__name__, duration.total_seconds()))
return ret
return wrapper
@logger
def add(x,y):
"""I am add."""
time.sleep(2)
return x + y
print(add(5, y = 8))
print(add.__name__, '|||', add.__doc__)
function add took 2.0s.
13
add ||| I am add.
4.6 第六版,增加函数执行时间判断
from functools import update_wrapper, wraps
import datetime
import time
def logger(duration=5):
def _logger(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
"""I am wrapper."""
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
if delta > duration:
print('function {} took {:.2f}s.'.format(fn.__name__, delta))
else:
print('function {} run so fast.'.format(fn.__name__))
return ret
return wrapper
return _logger
@logger(3)
def add(x,y):
"""I am add."""
time.sleep(2)
return x + y
print(add(5, y = 8))
print(add.__name__, '|||', add.__doc__)
function add run so fast.
13
add ||| I am add.
4.7 第七版,把之前函数进行抽象
from functools import update_wrapper, wraps
import datetime
import time
def x(delta, func, duration):
if delta > duration:
print('function {} took {:.2f}s. It run too slow.'.format(func.__name__, delta))
def logger(duration=2, func=x):
def _logger(fn):
@wraps(fn) # wrapper = update_wrapper(fn)(wrapper)
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
func(delta, fn, duration)
return ret
return wrapper
return _logger
@logger()
def add(x,y):
time.sleep(3)
return x + y
print(add(5, y = 8))
print(add.__name__, '|||', add.__doc__)
function add took 3.00s. It run too slow.
13
add ||| None