本篇博文主要介绍一些高级的函数特性,如map,reduce,filter,sort,装饰器,lambda等

1. map

这个map是指map/reduce中的map,而不是类似jdk的map数据结构

在python中,map接收两个参数,第一个为函数,第二个为一个可迭代的对象,作用是顺序的将迭代器中的元素丢给函数执行,并将结果作为新的Iterator返回

如将列表中的每个数求平方

1
2
3
4
5
6
7
8
9
10
# 列表生成式的写法
>>> [ x*x for x in range(1,6)]
[1, 4, 9, 16, 25]

# 使用map的方法
>>> def f(x):
... return x * x
...
>>> list(map(f, range(1,6)))
[1, 4, 9, 16, 25]

上面这样对比之后,发现列表生成式的写法更加简洁,并不能凸显map的优越性

换一个例子,将奇数采用*2,偶数采用平方的方式,则用列表生成式不太好写了

1
2
3
4
5
6
7
8
>>> def f(x):
... if(x % 2==0) :
... return x * x
... else:
... return x *2
...
>>> list(map(f, range(1,6)))
[2, 4, 6, 16, 10]

2. reduce

map相当于是顺序的执行迭代器中的元素;而reduce则是每个元素执行完毕之后,会与下一个元素一起进行计算,最终返回的是函数最终的计算结果

一个典型的case如,获取列表中元素的和

1
2
3
4
5
6
7
# 首先是导入依赖
>>> from functools import reduce
>>> def f(x, y):
... return x + y
...
>>> reduce(f, range(1, 100))
4950

需要额外注意的是使用reduce需要引入对应的包

3. filter

过滤,同样接收两个参数,第一个为过滤函数(返回True/False,True表示保留);第二个为可迭代的对象

如过滤数组中的所有偶数,只保留奇数

1
2
3
4
5
>>> def f(s):
... return s % 2 == 1
...
>>> list(filter(f, range(1, 10)))
[1, 3, 5, 7, 9]

4. sorted

sorted函数可以实现针对列表的排序,也可以接收一个key函数来实现自定义的排序,如按照绝对值大小进行排序

1
2
3
>>> l=[36, 5, -12, 9, -21]
>>> sorted(l, key=abs)
[5, 9, -12, -21, 36]

5. 匿名函数 lambda

使用lambda来修饰匿名函数,一般就是一个表达式,配合map/reduce等函数使用时,可能会非常简洁

语法

lambda 参数: 执行逻辑

如针对列表中,每个数求平方后得出新的列表

1
2
>>> list(map(lambda x: x*x, range(1, 5)))
[1, 4, 9, 16]

将前面的求和进行改造

1
2
>>> reduce(lambda x,y:x+y, range(1, 100))
4950

6. 装饰器

在python中这个装饰器的概念更加类似java中的代理模式,可以增强函数的某些操作,在实际使用中,和我们通常说的切面比较像

因为在python中函数可以作为变量来传参使用,因此装饰器模式的实质就是包装一下需要执行的方法,然后在这个方法执行前后做一些事情

实例1:

通过装饰器模式来统计方法的执行耗时

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
import functools
import time

## 统计方法执行时间
print('----------------------- time start -----------------')


def metric(func):
@functools.wraps(func)
def wrapper(*args, **kw):
start = time.time()
try:
return func(*args, **kw)
finally:
end = time.time()
print('%s() execute cost: %f() s' % (func.__name__, (end - start)))

return wrapper


@metric
def timeCal():
try:
print('time cal now!')
time.sleep(1)
except InterruptedError as e:
print(e)
return 'hello world'


print('res', timeCal())

try:
time.sleep(2)
except InterruptedError as e:
print(e)

print('----------------------- time over -----------------')

看下上面的metric方法,就是具体的装饰器实现方式,在需要引用的函数上面加上 @metric 即可了;需要额外注意的是在metric函数内部的wrapper函数上,多加了一行`@functools.wraps(func),主要是针对直接使用metric(timeCall)的调用方式时,返回的函数的签名依然为timeCall`,具体相关逻辑,参考: python教程之装饰器

上面执行结果的输出如下

1
2
3
4
5
----------------------- time start -----------------
time cal now!
timeCal() execute cost: 1.004160() ms
res hello world
----------------------- time over -----------------

实例2:

打印函数执行的日志(如常见的提供rpc服务,输出函数执行时的请求参数和返回结果), 我们现在考虑这个装饰器可以自主选择是否传参的case

看下面装饰器的具体实现中,首先是判断logger参数,如果是函数方式,则表示注解上没有额外参数,因此返回的是 decorate(prefix);否则返回decorate,两者之间的区别就是一个传参层级的问题

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
## logger 方法

print('----------------------- logger start -----------------')


def logger(prefix):
def decorate(func):
@functools.wraps(func)
def wrapper(*args, **kw):
if not hasattr(prefix, '__call__'):
print("prefix %s() req %s(), %s(): " % (prefix, args, kw))
else:
print("method %s() req %s(), %s(): " % (func.__name__, args, kw))

return func(*args, **kw)

return wrapper

# 如果logger没有传参,则直接走if逻辑
if hasattr(prefix, '__call__'):
return decorate(prefix)
else:
return decorate


@logger('selfCal')
def selfCal():
print('cal1....')


@logger
def selfCal2(text):
print('cal2....', text)


selfCal()
selfCal2("2222")

print('----------------------- logger end -----------------')

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals