高阶函数和装饰器

  • 高阶函数和装饰器
  1. 高阶函数 :

满足以下条件之一的称为高阶函数

  • 接受一个或多个函数作为参数
  • 输出一个函数

高阶函数举例:

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)装饰器和高阶函数

装饰器是高阶函数,但是装饰器是对传入函数的功能的增强。

  • 带参装饰器
    1. 是一个函数
    2. 函数作为他的形参
    3. 返回值是一个不带参的装饰器函数
    4. 使用@functionname方式调用
    5. 可以看作是在装饰器外层又加了一层函数

 

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模块

  • 函数注解
    1. 对函数的参数进行类型注解
    2. 对函数的返回值进行类型注解
    3. 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
    4. 提供给第三方工具,做代码分析,发现隐藏的bug
    5. 函数注解的信息,保存在__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对象

  1. 保存在元组中,是只读的
  2. name,参数的名字
  • annotation,参数的注解,可能没有定义
  1. default,参数的缺省值,可能没有定义
  2. empty,特殊的类,用来标记default属性或者注释annotation属性的空值
  3. kind,实参如何绑定到形参,就是形参的类型
    1. POSITIONAL_ONLY,值必须是位置参数提供(python未提供)
    2. POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提供
    3. VAR_POSITIONAL,可变位置参数,对应*args
    4. KEYWORD_ONLY,keyword-only参数,对应*或者*args之后的出现的非可变关键字参数
    5. 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方法
    1. 偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一
      个新的函数并返回
    2. 从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)

  1. Least-recently-used装饰器。lru,最近最少使用。cache缓存
  2. 如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是二的幂
    时,LRU功能执行得最好
  3. 如果typed设置为True,则不同类型的函数参数将单独缓存。例如,f(3)和f(3.0)将被视为具有不
    同结果的不同调用

最适合@ functools.lru_cache(maxsize=128, typed=False)的场景,用递归函数求斐波那契数列。

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/96909

发表评论

登录后才能评论

This site uses Akismet to reduce spam. Learn how your comment data is processed.

联系我们

400-080-6560

在线咨询:点击这里给我发消息

邮件:1823388528@qq.com

工作时间:周一至周五,9:30-18:30,节假日同时也值班