[toc]
python高阶函数和装饰器一
- First Class Object;
1.函数在python中是一等公民;
2.函数也是对象,可调用的对象;
3.函数可以作为普通变量、参数、返回值等;
- 高阶函数
1.数据概念y=g(f(x))
;
2.在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数;
接受一个多个函数作为参数;
输出一个函数;
def counter(base): def inc(step=1): nonlocal base base = base + step return base return inc foo = counter(10) foo1 = counter(10) print(foo()) print(foo1())
自定义sort 函数
- 排序问题
1.仿照内建函数sorted, 请自行实现一个sort函数(不使用内建函数),能够为列表元素排序;
- 思路
1.内建函数sorted函数是一个返回一个新的列表, 可以设置升序或降序,也可以设置一个 排序的函数;自定义的sort函数也要实现这个功能;
2.新建一个列表, 遍历原列表,和新列表的值依次比较决定如何插入新列表中;
- 思考
sorted 函数的实现原理,扩展到map、 filter函数的实现原理;
def sort(iterable,key=lambda a,b:a<b, reverse=False): ret = [] for x in iterable: for i,y in enumerate(ret): flag = key(x, y) if reverse else key(y,x) if flag: ret.insert(i,x) break else: ret.append(x) return ret lst = [1,2,3,11,5,6,4,8,9] print(sort(lst,reverse=True)) # 输出 [1, 2, 3, 4, 5, 6, 8, 9, 11]
内建函数-高阶函数
sorted(iterable[,key][,reverse])
排序filter(function, iterable) --> filter objcet
过滤数据map(func, *iterables) --> map object
映射
sorted(iterable[, key][, reverse]) 排序
返回一个新的列表,对一个可迭代对象的所有元素排序,排序规则为key定义的函数,reverse表是否排序翻转sorted(lst,key=lambda x:6-x)
# 返回新列表lst.sort(key=lambda x:6-x)
# 就地修改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)))
柯里化(Currying)
柯里化
指的是将原来接受两个参数的函数变成新的一接受一个参数的过程。新的函数返回一个以原有第二个参数的函数;z = f(x,y)
转换成z= f(x)(y)
的形式举例
将加法函数柯里化
def add(x,y): return x +y
In [1]: def add(x): ...: def _add(y): ...: return x + y ...: return _add In [2]: foo = add(4) In [3]: print(foo(5)) 9 In [4]: print(add(4)(5)) 9
通过嵌套函数就可以把函数转换成柯里化函数
Python 装饰器
- 需求
一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息;
In [5]: def add(x,y): ...: return x + y
增加信息输出功能
In [6]: def add(x,y): ...: print("calladd, x+y") # 日志输出到控制台 ...: return x+y
上面的加法函数是完成了需求,但是有以下的缺点
- 打印语句的耦合太高;
- 加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中;
In [10]: def add(x,y): ...: print("call {},{}+{}".format(add.__name__,x,y)) ...: return x + y ...: add(4,5) ...: call add,4+5 Out[10]: 9
做到了业务功能分离,但是fn函数调用传参是个问题
def add(x,y): return x + y def logger(fn): print('begin') x =fn(4,5) print('end') return x print(logger(add)) begin end 9
def add2(x,y,z): return x+y+z def logger(fn,*args,**kwargs): print('before') ret = fn(*args,**kwargs) print('after') return ret print(logger(add2,4,z=5,y=6))
def add1(x,y): return x + y def add2(x,y,z): return x+y+z def logger(fn,*args,**kwargs): def __logger(*args,**kwargs): print('before') ret = fn(*args,**kwargs) print('after') return ret return __logger foo = logger(add1) print(foo(40,10)) print(logger(add1)(100,500)) # 输出 before after 50 before after 600
改进: 引入装饰器
- 装饰器语法糖
def logger(fn): def __logger(*args,**kwargs): print('before') ret = fn(*args,**kwargs) print('after') return ret return __logger @logger # 相当于 add1 = logger(add1) def add1(x,y): return x + y print(add1(100,1000)) # 输出 before after 1100
写个装饰器,它就会把下面的的函数名提取出来,传给这个名称;
- 装饰器(无参)
它是一个函数
函数作为它的形参
返回值也是一个函数@functionname方式,简化调用
- 装饰器和高阶函数
装饰器是高阶函数,但装饰器是对传函数的功能的装饰(功能增强)
例子:
import datetime import time def logger(fn): def wrap(*args,**kwargs): print("*args={},kwargs={}".format(args,kwargs)) start = datetime.datetime.now() ret = fn(*args,**kwargs) # after 功能增强 duration = datetime.datetime.now() - start print("function {} took {}s.".format(fn.__name__,duration.total_seconds())) return ret return wrap @logger # 相当于 add = logger(add) def add(x,y): print("====call add ========") time.sleep(2) return x + y print(add(4,y=7)) # 输出结果 ====call add ======== function add took 2.00511s. 11
- 装饰器函数
- 前置功能增强
- 被增强函数
- 后置功能增强
文档字符串
- Python 的文档
- Python 是文档字符串的Documentation Strings;
- 在函数语句块的第一行,且习惯是多行的文本,所以多使用三引导;
- 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;
- 可以使用特殊属性doc访问这个文档;
def add1(x,y): '''This is a function return int x int y int ''' ret = x + y return ret add1(4,1000) print(add1.__name__,add1.__doc__,sep='\n') # 输出 add1 This is a function return int x int y int
注: print(help(add1))
函数本身就是调的__doc__
Help on function add1 in module __main__: add1(x, y) This is a function return int x int y int None
#!/usr/bin/env python def copy_properties(src,dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ dst.__qualname__ = src.__qualname__ def logger(fn): def wrapper(*args,**kwargs): '''This is a wrapper''' print('before') ret = fn(*args,**kwargs) print('after') return ret copy_properties(fn,wrapper) return wrapper @logger def add(x,y): ''' This is a function return int x int y int ''' ret = x + y return ret print(add.__name__,add.__doc__,add.__qualname__,sep='\n') # 输出 add This is a function return int x int y int add
- 装饰器副作用
原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性;
def copy_properties(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__name__ dst.__qualname__ = src.__qualname__ return dst return _copy def logger(fn): @copy_properties(fn) # @ copy => wrapper = _copy(wrapper) def wrapper(*args,**kwargs): '''This is a wrapper''' print('before') ret = fn(*args,**kwargs) print('after') return ret return wrapper @logger def add(x,y): ''' This is a function return int x int y int ''' ret = x + y return ret print(add.__name__,add.__doc__,add.__qualname__,sep='\n') # 输出 add add add
- python 提供一个函数,被封装函数属性 == copy ==> 包装函数属性;
- 能过copy_properties函数将被包装函数的属性覆盖掉包装函数;
- 凡是被装饰的函数都城要复制这些属性,这个函数很通用;
- 可以将复制属性的函数构建成装饰器函数,带参装饰器;
带参装饰器
import datetime import time def logger(t): def __logger(fn): def wrap(*args,**kwargs): start = datetime.datetime.now() ret = fn(*args,**kwargs) duration = (datetime.datetime.now() - start).total_seconds() if duration > t: print("function {} took {} s.".format(fn.__name__,duration)) return ret return wrap return __logger @logger(3) def add(x,y): print("===call add===") time.sleep(5) return x + y print(add(4, y=7)) # 输出 ===call add=== function add took 5.001952 s. 11
- 带参装饰器
- 它是一个函数;
- 函数作为它的的形参;
- 返回值是一个不带参的装饰器函数;
- 使用@functionname(参数列表)方式调用;
- 可以看做在装饰器外层又加了一层函数;
- 将记录的功能提取出来,这样就可以能过外部提供的函数来灵我没有的控制输出
- 再次改造
import datetime import time def copy_properties(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__name__ dst.__qualname__ = src.__qualname__ return dst return _copy def logger(duration,func=lambda name,duration: print("{} took {}s".format(name,duration))): def _logger(fn): @copy_properties(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(3) def add(x,y): print("===call add===") time.sleep(5) return x + y print(add(4, y=7)) # 输出 ===call add=== add took 3s 11
functools 模块
functools.update_wrapper(wrapper,wrapped, assigned=WRAPPER_ASSGNMENTS, update=WRAPPER_UPDATES)
- 类似
copy_properties
功能; - wrapper 包装函数,wrapped被包装函数;
- 元组
WRAPPER_ASSIGNMENTS
中是要被覆盖的属性;
'__module__','__name__','__qualname__','__doc__','__annotations__' 模块名、名称、限定名、文档、参数注解
- 元组WRAPPERUDPATES中是要被更新的属性, `_dict`属性字典
- 增加一个
__wapped__
属性,保留着wrapped函数;
funtools.update_wrapper
实例
def copy_properties(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__name__ dst.__qualname__ = src.__qualname__ return dst return _copy import functools def logger(fn): def wrapper(*args,**kwargs): '''This is a wrapper''' print('before') ret = fn(*args,**kwargs) print('after') return ret functools.update_wrapper(wrapper,fn) return wrapper @logger def add(x,y): ''' This is a function return int x int y int ''' ret = x + y return ret print(add.__name__,add.__doc__,add.__qualname__,sep='\n') print('#'*50) print(add.__wrapped__) ## 输出 add This is a function return int x int y int add ################################################## <function add at 0x11539e488>
格言
凡人做一事,便须全副精神注在些一事,首尾不懈,不可见异思迁,做这样想那样,坐之山望 那山,人而无恒,终身一事无成