Python-3.高阶函数(高级篇)

高阶函数

函数作为返回值 或者 函数作为参数 这类函数就是高阶函数。上节博文有说过闭包,递归都是属于高阶函数。

函数作为返回值:通常用作闭包的场景,需要封装一些变量,后面的面向对象会有类来封装,通常用返回作为返回值很少

函数作为参数:通常用于大多数逻辑固定,少部分逻辑不固定的场景

通常 函数作为参数 用的比 函数作为返回值 多,函数返回值封装通常用面向对象来使用

标准库中函数作为参数用的很多。

python中函数式一等对象(first class),函数也是一种对象,并且和普通对象一样赋值,作为参数作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#实例:
def counter(i):
base = i
def inc(x=1):
nonlocal base
base += x
return base
return inc

inc = counter(3)
inc(3)

#如果实现sort排序?
def sorts(it):
ret = []
for i in it:
for k,v in enumerate(ret):
if i > v:
ret.insert(k,i)
break
else:
ret.append(i)
return ret
sorts([2,4,1,24,5,12,4,5])
out>[24, 12, 5, 5, 4, 4, 2, 1]
#这里enumerate是指枚举,如果列表是[2,3,4,5],枚举后就是[(0,2),(1,3),(2,4),(3,5)]
#第一次循环,i是2,k,v是0,0,将2插入(0,2)
#第二次循环,i是4,k,v是0,2,这里4>2,将0的位置给4,
#第三次循环,i是1,k,v是0,4,这里就往后添加
#是个逆序排序
#如果想实现,需要输入参数reverse=False,就从小到大排序,reverse=True就反过来
def sorts(it,reverse=True):
ret = []
for i in it:
for k,v in enumerate(ret):
if reverse:
if i>v:
ret.insert(k,i)
break
else:
if i<v:
ret.insert(k,i)
break
else:
ret.append(i)
return ret
#如果不输入reverse默认是从大到小排序
sorts([1,2,3,4,5,6])
#如果输入reverse=False就是从小到大排序
sorts([1,2,3,4,5,6],reverse=False)
##这个sorts排序并不是高阶函数,只是一个逻辑处理,如何写成高阶函数呢?
def sorts(lst,cmp=lambda a,b:a>b):
ret = []
for i in lst:
for k,v in enumerate(ret):
if cmp(i,v):
ret.insert(k,i)
break
else:
ret.append(i)
return ret
sorts([4,2,3,1,23,4,12])
out>[23, 12, 4, 4, 3, 2, 1]
sorts([4,2,3,1,23,4,12],cmp=lambda a,b:a<b)
out>[1, 2, 3, 4, 4, 12, 23]
#这就是高阶函数。

内置函数

匿名函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def fn(x):
return x+1
print(fn)
out><function fn at 0x7f95e965bea0>

lambda x:x+1
out><function __main__.<lambda>(x)>

#函数和匿名函数调用
def fn(x):
return x+1
fn(3)
out>4

(lambda x:x+1)(3)
out>4

#匿名函数
#使用lambda来定义,
#参数列表不需要括号
#跟函数不一样,:不是用来开启新的语句块
#最后一个表达式没有return

(lambda x:x+1)(3)
#第一个括号是定义一个匿名函数,第二个括号是函数调用

f = lambda x:x+1
f(3)
out>4
#可以将匿名函数赋值给一个变量,
#通过这个变量来调用这个函数

#匿名函数限制
#匿名函数(也叫lambda表达式)只能写一行,也叫单行函数(global不可用和nonlocal不可用)
#匿名函数只有一个表达式

#下面几个实例感受下lambda
(lambda:0)()
out>0
#没有参数也可以

(lambda x,y:x+y)(5,3)
out>8
#可使用位置参数

(lambda x=3,y=2:x-y)()
out>1
(lambda x=3,y=2:x-y)(5,2)
out>3
#也可以是用默认参数

lst=[1,2,3]
(lambda *args:args)(*lst)
#可以使用可变位置参数

dic={'a':1,'b':2}
(lambda **kwarg:kwarg)(**lst)
#也可以使用可变关键字参数

lst=[1,2,3]
dic={'a':1,'b':2}
(lambda *arg,**kwargs:print(arg,kwargs))(*lst,**dic)
#也可以一起使用,规则跟函数一样,参数可以解构

#lambda判断
(lambda x,y:x if x<y else y)(1,2)
其他内置函数
map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
help(map)
map(func, *iterables) --> map object

#将func应用到序列上个每一个元素上,返回一个值,不管这个序列原来是什么类型
#事实上,根据函数参数多少,map可以接受多个组序列,将其对应的元素作为参数传入函数


#例子[1,2,3][2,3,4]两个列表想加
def fn(x,y):
return x + y
a = [1,2,3]
b = [1,2,3]
map(fn,a,b)
>>>2,4,6


#map源码
def map_(fn,it):
return (fn(x) for x in it)

#如果用lambda写会更爽
a = [1,2,3]
b = [1,2,3]
list(map((lambda a,b:a+b),a,b))
filter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
help(filter)
filter(function or None, iterable) --> filter object

#filter和map类似,filter也接受一个函数和一个序列,和map()不同的是,filter()把传入的函数依次作用每个元素,
#根据返回值是ture还是false决定保留还是丢弃

例子
list(filter(lambda x:x%2==0,range(10)))
>>>[0,2,4,6,8]

list(filter(lambda x:x and x.strip(),['a','','c’,None,'']))
>>>[‘a’,’c’]

#filter源码
def fiter_(fn,it):
return (x for x in it if fn(x))
reduce

在python3中,reduce函数从全局空间中移除,防止在了functools模块中,需要引入

reduce就是把一个函数 用在一个列表序列上,这个函数必须接受两个参数,reduce把结果继续和下一个序列的下一个元素累计运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from tunctools import reduce
help(reduce)
reduce(function, sequence[, initial])

#例子
#计算一个列表的和
lst = [1,23,4,5,12,3]
def fn(x,y):
return x+y
reduce(fn,lst)
out>48

lst = [1,2,3,4,5]
def fn(x,y):
return x*10+y
reduce(fn,lst)
out>12345


#实例:
#将字符转换成数字
def fn(x,y):
return x*10+y
str2num ={str(x):y for x,y in enumerate(range(11))}
def s2n(s):
return str2num[s]
reduce(fn,map(s2n,'1987'))

#改成lambda表达式
def str2num(z):
def s2n(s):
dic = {str(x):y for x,y in enumerate(range(11))}
return dic[s]
return reduce((lambda x,y:x*10+y),map(s2n,z))

装饰器

顾名思义就知道是一种装饰作用,装饰有很多种,比如计算函数时长,在函数结果前或者结果后做一些装饰。

装饰器其实只是高阶函数的一种,分为不带参数的装饰器,带参数的装饰器。

高阶函数:函数作为返回值 或者 函数作为参数 这类叫高阶函数

装饰器:函数即作为返回值 又 作为参数,这叫装饰器(装饰器的参数必须是函数,返回值必须也是函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#例子
#如何计算一个函数执行的时长
import time
def func():
time.sleep(2)
def counter(fn):
def inner():
start = time.time()
fn()
return time.time()-start
return inner
f = counter(func)
f()
out>2.0002...


#python提供一个语法糖@,可以这么来写
def counter(fn):
def inner():
start = time.time()
fn()
return time.time()-start
return inner
@counter
def func1():
time.sleep(2)
func1()
out>2.0020...
#这里的@counter相当于func1=counter(func1)



#函数添加参数
def counter(fn):
def inner(x):
start=time.time()
ret = fn(x)
print(time.time()-start)
return ret
return inner
@counter
def add(x):
x += 1
time.sleep(2)
return x
a = add
a(2)
2.002060651779175
out>3

#函数添加多个参数
def counter(fn):
def inner(*arg):
start=time.time()
ret = fn(*arg)
print(time.time()-start)
return ret
return inner
@counter
def add(x,y):
add = x+y
time.sleep(2)
return add
a = add
a(2,5)
2.002060651779175
out>7
#counter接受fn函数,inner接受fn函数参数,函数fn式具体实现功能,只不过函数fn被counter装饰了
#这里counter接受add的函数。inner接受add参数,函数add实现了功能,不过add被counter装饰了

#执行流程:首先是一层层传进去再一层层返回出来:
#counter(add(2,5))===>inner(2,5)===>add(2,5)===>inner(2,5)===>counter(add(2,5))

#装饰器参数,比如计算一个函数,如果超过3秒打印超时,如果没超过就显示正常
import time
def logg(timeout):
def counter(fn):
def inner(*arg):
start=time.time()
ret = fn(*arg)
end = time.time()
if (end-start) >= timeout:
return '函数执行超时'.format(timeout),ret
else:
return ret
return inner
return counter
@logg(2)
def foo(x):
time.sleep(x)
return x
foo(1)
out>1
foo(5)
out>('函数执行超时', 5)

总结

装饰器本质就是一个函数,接受一个函数作为参数,并返回一个函数

装饰器通常会返回一个封装函数,这个封装在传入的函数前后作一些事情

装饰器本身肯定必须是高阶函数

装饰器所装饰的函数就是装饰器所接受的参数

函数属性如何保留
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#看个简单实例:
def timeit(fn):
def wrap(*args,**kwargs):
start = time.time()
ret = fn(*args,**kwargs)
print(time.time() - start)
return ret
return wrap
@timeit
def func(x):
time.sleep(x)
return x
func.__name__
out>'wrap'
#这里发现本身func是个函数为啥这里变成了warp
help(func)
wrap(*args, **kwargs)

#如何保存原来的属性呢?
def timeit(fn):
def wrap(*args,**kwargs):
start = time.time()
ret = fn(*args,**kwargs)
print(time.time() - start)
return ret
wrap.__name__ = fn.__name__
return wrap
@timeit
def func(x):
time.sleep(x)
return x
func.__name__
out>'func'
help(func)
#这里只是简单的改了函数__name__
help(func)
wrap(*args, **kwargs)

#如何修改所有的属性呢?
from functools import wraps
def timeit(fn):
@wraps(fn)
def wrap(*args,**kwargs):
start = time.time()
ret = fn(*args,**kwargs)
print(time.time() - start)
return ret
return wrap
@timeit
def func(x):
time.sleep(x)
return x
func.__name__
func(x)

装饰器实例

实现cache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from functools import wraps
import time
def cache(instance):
def dec(fn):
@wraps(fn)
def wrap(*args,**kwargs):
pos = ','.join((str(x) for x in args))
kw = ','.join('{}={}'.format(k,v) for k,v in sorted(kwargs.items()))
key = '{}::{}::{}'.format(fn.__name__,pos,kw)
ret = instance.get(key)
if ret is not None:
return ret
ret = fn(*args,**kwargs)
instance.set(key,ret)
return ret
return wrap
return dec

class DictCache:
def __init__(self):
self.cache = dict()
def get(self,key):
return self.cache.get(key)
def set(self,key,value):
self.cache[key] = value
def __str__(self):
return str(self,cache)
def __repr__(self):
return repr(self,cache)

cache_instance = DictCache()
#字典保存cache
@cache(cache_instance)
def long_time_fun(x):
time.sleep(x)
return x

long_time_fun(3)
out>3 #第一次执行保存3s
long_tiem_fun(3)
out>3 #第二次就直接返回不需要等待

#通常这种功能不需要写,python3标准库提供该模块
from functools import lru_cache
help(lru_cache)
lru_cache(maxsize=128,type=False)
#定义最大值,如果超出128就,使用少就剔除
@lru_cache()
def long_time_fun(x):
time.sleep(x)
return x
long_time_fun(3)
long_time_fun(3)
#跟上面类似,第一次需要3s,第二次直接返回
监控
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def mertic(prefix,instance):#prefix是发送的前缀,instance发送监控信息
def timeit(fn):
@wrap(fn)
def wrap(*args,**kwargs):
start = time.time()
ret = fn(*args,**kwargs)
key = '{}.{}.{}'.format(prefix,fn.__module__,fn.__name__)
instance.send(key,time.time()-start) #send发送到那边
return ret
return wrap
return timeit

import logging

class LogginMetric:
import logging
def send(self,key,value):
logging.warning('{}=>{}'.format(key,values))

@mertic(perfix='tomcat',instance=LoggingMetric())
def long_time_fun(x):
time.sleep(x)
return x

long_time_fun(1)
1
#statsd放到influxdb,前段用grafana
身份验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def auto_func(func):                                          
def wrapper(*args,**kwargs):
user = input('please username:').strip()
passwd = input('please password:').strip()
if user == 'zhuxuyue' and passwd == '123':
rest = func(*args,**kwargs)
return rest
else:
print('用户名密码错误')
return wrapper

def index():
print('欢迎访问taoboa.com')
@auto_func
def home(name):
print('欢迎{}来到taobao.com'.format(name))
@auto_func
def shopping_car(name):
print('{}的购物车有{}{}'.format(name,'mike','computer'))

index()
home('朱旭悦')
shopping_car('朱旭悦')
#每次执行都需要登录,如何提供session功能嫩?

#这里需要将输入的密码存储起来。
user_dict = {'username':None,'Login':False}
def auto_func(func):
def wrapper(*args,**kwargs):
if user_dict['username'] and user_dict['Login']:
rest = func(*args,**kwargs)
return rest
user = input('please username:').strip()
passwd = input('please password:').strip()
if user == 'zhuxuyue' and passwd == '123':
user_dict['username'] = 'zhuxuyue'
user_dict['Login'] = True
rest = func(*args,**kwargs)
return rest
else:
print('用户名密码错误')
return wrapper

def index():
print('欢迎访问www.taoboa.com')
@auto_func
def home(name):
print('欢迎{}来到taobao.com'.format(name))
@auto_func
def shopping_car(name):
print('{}的购物车有{}{}'.format(name,'mike','computer'))

index()
home('朱旭悦')
shopping_car('朱旭悦')
获取最大值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#模拟数据源不断产生数字,求一段时间内,最大的元素
import random #随机数模块
import time #时间戳模块
import datetime #时间模块
def data_source():
while True:
yield random.randint(0,100)
time.sleep(0.1)
ds = data_source()
for _ in range(10):
print(next(ds))
#模拟数据源代码

#求一段时间最大元素,top k
def top_k1(k,time=3):
start = datetime.datetime.now()
lst = []
while True:
lst.append(next(ds))
current = datetime.datetime.now()
if (current - start).total_seconds() >= time:
start = current
lst.sort()
ret = []
for _ in range(k):
ret.append(lst.pop())
yield ret
g = top_k1(10)
for _ in range(3):
print(next(g))

#效率比较慢,需要全部加入列表后才能算出结果.

#如何使用堆实现
import datetime
import random
import time
def heap():
data = []
def add(e):
idx = len(data)
data.append(e)
parent_idx = (idx - 1) // 2
while parent_idx >= 0:
if data[idx] > data[parent_idx]:
data[parent_idx],data[idx] = data[idx],data[parent_idx]
idx = parent_idx
parent_idx = (idx - 1) // 2
else:
break
def pop():
if not data:
return None
if len(data) == 1:
return data.pop()
idx = 0
ret = data[idx]
data[idx] = data.pop()
left_idx = 2 * idx + 1
rigth_idx = left_idx + 1
while left_idx < len(data):
child_idx = left_idx
if rigth_idx < len(data) and data[rigth_idx] > data[left_idx]:
#存在有子节点,并且又子节点大于左子节点
child_idx = rigth_idx
if data[idx] < data[child_idx]:
data[idx],data[child_idx] = data[child_idx],data[idx]
idx = child_idx
left_idx = 2*idx+1
rigth_idx = left_idx + 1
else:
break
return ret
return add,pop


def data_source():
while True:
yield random.randint(0, 100)
time.sleep(0.1)
ds = data_source()

def top_k3(k,time=3):
start = datetime.datetime.now()
add,pop = heap()
while True:
add(next(ds))
current = datetime.datetime.now()
if (current - start).total_seconds() >= time:
start = current
ret = []
for _ in range(k):
ret.append(pop())
yield ret
g3 = top_k3(10)
for _ in range(3):
print(next(g3))

#堆是优先队列。先进先出
看完了?赏个鸡腿钱,谢谢老板!