- 高阶函数和装饰器
- 高阶函数 :
满足以下条件之一的称为高阶函数
- 接受一个或多个函数作为参数
- 输出一个函数
高阶函数举例:
def counter(base):
def inc(step=1):
nonlocal base
base += step
return base
return inc
1)自定义sort函数
def sort(itertable):
ret = []
for i in itertable :
#print(i)
for m,n in enumerate(ret):
if i < n :
ret.insert(m,i)
break
else :
ret.append(i)
return ret
sort([1,9,8,7,5,6,4,2,3])
2)内建函数-高阶函数
sorted 排序 sorted(iterable[, key][, reverse])
filter 过滤数据 filter(function, iterable) –> filter object
map 映射 map(func, *iterables) –> map object
sorted函数返回一个新的list 而list.sort是就地修改的
filter(function, iterable)
过滤可迭代对象的元素,返回一个迭代器
function一个具有一个参数的函数,返回bool例如,过滤出数列中能被3整除的数字
list(filter(lambda x: x%3==0, [1,9,55,150,-3,78,28,123]))
map(function, *iterables) –> map object
对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器
list(map(lambda x:2*x+1, range(5)))
dict(map(lambda x: (x%5,x) , range(500)))
- 柯里化
柯里化指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数
z = f(x,y)转换成z = f(x)(y)
举例
def add(x,y):
return x+y
add(4,6)
转换
def add(x)
def _add(y)
return x+y
return _add
add(4)(6)
三 装饰器
装饰器语法糖 @
1)无参装饰器 :
a)他是一个函数
b)函数作为形参
c)返回值也是一个函数
d)可以使用@functiname的方式,简化调用
无参装饰器例
import time
import datetime
def logger(fn):
def wrapper(*args,**kwargs) :
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
print(delta)
return ret
return wrapper
@logger
def add(x,y):
”’This is an add
function”’
time.sleep(2)
return x * y
print(add(4,6))
2)装饰器和高阶函数
装饰器是高阶函数,但是装饰器是对传入函数的功能的增强。
- 带参装饰器
- 是一个函数
- 函数作为他的形参
- 返回值是一个不带参的装饰器函数
- 使用@functionname方式调用
- 可以看作是在装饰器外层又加了一层函数
import time
import datetime
def copy_(str):
def _inc(dest):
dest.__name__ = str.__name__
dest.__doc__ = str.__doc__
return _inc
def logger(duration,func = lambda name,duration:print(‘{} tooks {} s’.format(name,duration))):
def _logger(fn):
@copy_(fn)
def wrapper(*args,**kwargs) :
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
print(‘fast’) if delta < duration else print(‘slow’)
#print(delta)
func(fn.__name__,delta)
return ret
return wrapper
return _logger
@logger(3)
def add(x,y):
”’This is an add
function”’
time.sleep(2)
return x * y
print(add(4,6))
print(add.__name__,add.__doc__)
四,functools模块
functools.update_wrapper(wrapper, wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
@functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
- 类似copy_properties功能
- wrapper 包装函数、被更新者,wrapped 被包装函数、数据源
- 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性
‘__module__’, ‘__name__’, ‘__qualname__’, ‘__doc__’, ‘__annotations__’
模块名、名称、限定名、文档、参数注解 - 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
- 增加一个__wrapped__属性,保留着wrapped函数
两个函数的对比如下
函数一
import datetime, time, functools
def logger(duration, func=lambda name, duration: print(‘{} took {}s’.format(name, duration))):
def _logger(fn):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__, duration)
return ret
functools.update_wrapper(wrapper, fn)
return wrapper 等价 # return functools.update_wrapper(wrapper, fn)
return _logger
@logger(5) # add = logger(5)(add)
def add(x,y):
time.sleep(1)
return x + y
print(add(5, 6), add.__name__, add.__doc__, sep=’\n’)
函数二
import datetime, time, functools
def logger(duration, func=lambda name, duration: print(‘{} took {}s’.format(name, duration))):
def _logger(fn):
@functools.wraps(fn)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__, duration)
return ret
return wrapper
return _logger
@logger(5) # add = logger(5)(add)
def add(x,y):
”’This is a add”’
time.sleep(1)
return x + y
print(add(5, 6), add.__name__, add.__doc__, sep=’\n’)
一般用第二种
五 参数注解,inspe模块
- 函数注解
- 对函数的参数进行类型注解
- 对函数的返回值进行类型注解
- 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
- 提供给第三方工具,做代码分析,发现隐藏的bug
- 函数注解的信息,保存在__annotations__属性中
def add(x:int , y:int) -> int :
”’
:param x: int
:param y: int
:return: int
”’
return x + y
print(help(add))
print(add(4, 5))
print(add(‘mag’, ‘edu’))
print(add.__annotations__)
2)inspect模块
a)signature(callable),获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息)
import inspect
def add(x:int, y:int, *args,**kwargs) -> int:
return x + y
sig = inspect.signature(add)
print(sig, type(sig)) # 函数签名
print(‘params : ‘, sig.parameters) # OrderedDict
print(‘return : ‘, sig.return_annotation)
print(sig.parameters[‘y’], type(sig.parameters[‘y’]))
print(sig.parameters[‘x’].annotation)
print(sig.parameters[‘args’])
print(sig.parameters[‘args’].annotation)
print(sig.parameters[‘kwargs’])
print(sig.parameters[‘kwargs’].annotation)
执行结果:
(x:int, y:int, *args, **kwargs) -> int <class ‘inspect.Signature’>
params : OrderedDict([(‘x’, <Parameter “x:int”>), (‘y’,<Parameter “y:int”>), (‘args’, <Parameter “*args”>), (‘kwargs’, <Parameter “**kwargs”>)])
return : <class ‘int’>
y:int <class ‘inspect.Parameter’>
<class ‘int’>
*args
<class ‘inspect._empty’>
**kwargs
<class ‘inspect._empty’>
b)parame对象
- 保存在元组中,是只读的
- name,参数的名字
- annotation,参数的注解,可能没有定义
- default,参数的缺省值,可能没有定义
- empty,特殊的类,用来标记default属性或者注释annotation属性的空值
- kind,实参如何绑定到形参,就是形参的类型
- POSITIONAL_ONLY,值必须是位置参数提供(python未提供)
- POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提供
- VAR_POSITIONAL,可变位置参数,对应*args
- KEYWORD_ONLY,keyword-only参数,对应*或者*args之后的出现的非可变关键字参数
- VAR_KEYWORD,可变关键字参数,对应**kwargs
sig.parameters返回一个有序字典OrderedDict
import inspect
def add(x, y:int=7, *args, z, t=10,**kwargs) -> int:
return x + y
sig = inspect.signature(add)
print(sig)
print(‘params : ‘, sig.parameters) # 有序字典
print(‘return : ‘, sig.return_annotation)
print(‘~~~~~~~~~~~~~~~~’)
for i, item in enumerate(sig.parameters.items()):
name, param = item
print(i+1, name, param.annotation, param.kind, param.default)
print(param.default is param.empty, end=’\n\n’)
执行结果:
(x, y:int=7, *args, z, t=10, **kwargs) -> int
params : OrderedDict([(‘x’, <Parameter “x”>), (‘y’, <Parameter “y:int=7”>), (‘args’, <Parameter “*args”>), (‘z’, <Parameter “z”>), (‘t’, <Parameter “t=10”>), (‘kwargs’, <Parameter “**kwargs”>)])
return : <class ‘int’>
~~~~~~~~~~~~~~~~
1 x <class ‘inspect._empty’> POSITIONAL_OR_KEYWORD <class ‘inspect._empty’>
True
2 y <class ‘int’> POSITIONAL_OR_KEYWORD 7
False
3 args <class ‘inspect._empty’> VAR_POSITIONAL <class ‘inspect._empty’>
True
4 z <class ‘inspect._empty’> KEYWORD_ONLY <class ‘inspect._empty’>
True
5 t <class ‘inspect._empty’> KEYWORD_ONLY 10
False
6 kwargs <class ‘inspect._empty’> VAR_KEYWORD <class ‘inspect._empty’>
True
业务应用(包装成装饰器)
import inspect
def check(fn):
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
params = sig.parameters
values = list(params.values())
for i,p in enumerate(args):
param = values[i]
if param.annotation is not param.empty and not isinstance(p, param.annotation):
print(p,’!==’,values[i].annotation)
for k,v in kwargs.items():
if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
print(k,v,’!===’,params[k].annotation)
return fn(*args, **kwargs)
return wrapper
@check
def add(x, y:int=7) -> int:
return x + y
#调用测试
print(add(20,10))
print(add(‘x‘,’y‘)) #会打印y !== <class ‘int’>
六偏函数 partia,lru_cache装饰器
- patial方法
- 偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一
个新的函数并返回 - 从partial生成的新函数,是对原函数的封装
- 偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一
partial 简易源码
def partial(func,*args,**keywords):
def newfunc(*fargs,**fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
print((args + fargs),newkeywords)
return func(*(args + fargs),**newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
def add(x, y, *args) -> int:
#print(args)
return x + y
newadd = partial(add,1,3,6,5)#func(1,3,6,5,15)
newadd(15)
如上定义partial的话ruturn的func为func(1,3,6,5,15)所以函数add的return值不变但是如果定义partial为partial(add,x = 4,y = 5)的话,partial的return就变成了一个字典{‘x‘:4,’y’:5}此时如果newadd(1,3)就会报错x,y重复赋值此时只能使用关键字传参。
2)@functools.lru_cache(maxsize=128, typed=False)
- Least-recently-used装饰器。lru,最近最少使用。cache缓存
- 如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是二的幂
时,LRU功能执行得最好 - 如果typed设置为True,则不同类型的函数参数将单独缓存。例如,f(3)和f(3.0)将被视为具有不
同结果的不同调用
最适合@ functools.lru_cache(maxsize=128, typed=False)的场景,用递归函数求斐波那契数列。
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/96909