1. 函数装饰器
memo封装了一个框架,fib和climb都用这个算法框架。@memo即可赋予他们这个功能。
def memo(func) :
# cache不会因为执行完memo而消失
cache = {}
def wrap(*args) :
if args not in cache :
cache[args]=func(*args)
return cache[args]
return wrap
@memo
def fib(n) :
if n <=1 :
return 1
return fib(n-1) + fib(n-2)
@memo
def climb(n , steps) : # 台阶阶数,可走的步数 (10 , [1,2,3])
count = 0
if n == 0 :
count = 1
elif n > 0 :
for s in steps :
count += climb(n - s , steps)
return count
print(fib(50))
print(climb(10 , (1,2,3)))
2. 为被包裹函数保存元数据
'''
函数的元数据:
f.__name__ 函数对象的名字,也就是def后面的函数名
f.__doc__ 函数的文档
f.__module__ 函数所属的模块
f.__defaults__ 函数的默认参数值
f.__closure__ 函数的闭包
'''
from functools import wraps
def mydecorator(func) :
@wraps(func) # 这个装饰器为被包裹函数保存了它的元数据
def wrapper(*args , **kargs) :
'''wrapper function '''
print(' In Wrapper')
func(*args , **kargs)
return wrapper
@mydecorator
def example():
'''example function'''
print('In example')
print(example.__name__)
print(example.__doc__)
3. 对函数参数调用进行类型检查
这段代码我看不太懂,索性直接抄下来,以后可以无脑复用就行。
### 定义一个带参数的装饰器
from inspect import signature
def typeassert(*ty_args , **ty_kargs) :
def decorator(func) :
# func -> a , b
# d = {'a' : int , 'b' : str}
sig = signature(func)
btypes = sig.bind_partial(*ty_args , **ty_kargs).arguments
def wrapper(*args , **kargs) :
# arg in d.
for name , obj in sig.bind(*args , **kargs).arguments.items() :
if name in btypes:
if not isinstance(obj , btypes[name]) :
raise TypeError('%s must be %s' % (name , btypes[name]))
return func(*args , **kargs)
return wrapper
return decorator
@typeassert(int,str,list)
def f(a,b,c) :
print(a,b,c)
f(1,'abc' , [1,2,3])
f(2,2,1)
3.5 对参数列表和返回值的检查
def typeassert(in_ = () , out = (type(None) , )) :
def _typeassert(func):
def check_run_check(*args):
# 检查参数列表
if type(func).__name__ == 'method' :
checkable_args = args[1:] # 除去self
elif type(func).__name__ == 'function' :
checkable_args = args
_check_types(checkable_args , in_)
# 执行
res = func(*args)
# 检查结果
if not type(res) in (tuple, list):
checkable_res = (res , )
else :
checkable_res = res
_check_types(checkable_res , out)
return res
def _check_types(elements, types):
""" subfunc that checks the types """
if len(elements) != len(types) :
raise TypeError('args count is wrong')
typed = enumerate(zip(elements,types))
for index , couple in typed :
arg , right_type = couple
if isinstance(arg , right_type):
continue
raise TypeError('arg #%d should be %s' %(index , right_type))
return check_run_check
return _typeassert
@typeassert((int,int) , (int,))
def add(a , b) :
return a + b
print(add(2,3))
# print(add(2.0,3.0))
4. 实现属性可修改的函数装饰器
这是一个体验装饰器的绝佳例子。
函数从外到里,先是装饰器接收的参数-被装饰函数作为参数传入-wrapper好比被装饰函数一样处理函数参数。
一共三层结构,可以细细琢磨。
setTimeout函数定义成wrapper的属性,真正使用的时候,也是被装饰函数直接调用它,逻辑通畅!
### 实现一个属性可以修改的函数装饰器
from functools import wraps
import time
import logging
def warn(timeout) : # 装饰器接收的参数
def decorator(func): # 接收被装饰的函数
def wrapper(*args , **kargs) : # 处理函数所收到的参数
start = time.time()
res = func(*args , **kargs)
used = time.time() - start
if used > timeout :
msg = " '%s' : %s > %s " % (func.__name__ , used, timeout)
logging.warn(msg)
return res
def setTimeout(k) :
nonlocal timeout
timeout = k
wrapper.setTimeout = setTimeout
return wrapper
return decorator
from random import randint
@warn(1.5)
def test():
print('In test')
while randint(0,1) :
time.sleep(0.5)
for _ in range(30) :
test()
test.setTimeout(1) # wrapper函数包含这被装饰函数的处理逻辑
# setTimeout 成为了被装饰函数的属性
for _ in range(30) :
test()