高阶函数和装饰器

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

(0)
上一篇 2018-04-23 08:51
下一篇 2018-04-23 10:27

相关推荐

  • python– 简单的排序

    冒泡排序, 简单选择排序, 插入排序

    Python笔记 2018-04-15
  • 元组与字符串

    元组tuple 一个有序的元素组成的集合,不可变,用()表示,可进行索引,正索引:从左至右,从0开始;负索引:从右至左,从-1开始,正负索引不可超界,否则引发indexerror,tuple[index],index就是索引,使用中括号访问 元组是只读的,所以没有增,改,删的方法 冒泡法:属于交换排序,两两比较大小,交换位置,结果分为升序和降序排列 升序:n…

    Python笔记 2018-04-01
  • 树 概念


    二叉树

    2018-04-16
  • 解析式

    列表解析式和字典解析式 datetime模块 对日期,时间,时间戳的处理 datetime类 today()返回本地时区当前的datetime对象 now(tz=None)返回当前时间的datetime对象,时间到微秒,如果tz为None,返回和today()一样 utcnow()没有时区的当前时间 fromtimestamp(timestamp,tz=Zo…

    2018-04-09
  • Python内置数据结构——字符串

    知识结构图 学习笔记 字符串 字符组成的有序序列,字符的集合 使用单引号、双引号、三引号引起来的字符序列 不可变对象 Unicode类型 定义 单引号、双引号、三引号 r、R:引号内的字符原样输出 元素访问 索引访问 可迭代 join连接 “string“.join(iteratable) 使用string作为分隔符将可迭代对象连接起…

    2018-03-31
  • 面向对象之魔术方法

    特殊属性 属性 含义 __name__ 类、函数、方法的名称 __module__ 类定义所在的模块名 __class__ 对象或类所属的类 __bases__ 类的基类的元组,顺序为它们在基类列表中出现的顺序 __doc__ 类、函数的文档字符串,若没定义则为None __mro__ 类的mro,方法查找顺序 __dict__ 类或实例的属性,可写的字典 …

    2018-05-20