[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>
格言
凡人做一事,便须全副精神注在些一事,首尾不懈,不可见异思迁,做这样想那样,坐之山望 那山,人而无恒,终身一事无成