函数式编程
允许把函数本身作为参数传入另一个函数,还允许返回一个函数
对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数
map函数:(作用于每一个元素,参数:函数,序列)
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
a = map(abs, [-10, -20, 30, -40])
print(list(a))
Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list
def func2(x):
return x * x
b = map(func2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(list(b))
reduce函数:(累计计算,参数:函数,序列)
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
from functools import reduce
def func3(x, y):
return x + y
print(reduce(func3, [1, 2, 3, 4, 5, 6, 7, 8])) # 36
def func4(x, y):
return x * 10 + y
print(reduce(func4, [1, 2, 3, 4, 5, 6, 7, 8]))
把字符串转换为int
def func5(x, y):
return x * 10 + y
def strtoint(s):
digit = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
return digit[s]
print(reduce(func5, map(strtoint, '56923')))
# 内置函数转换为int
print(type(int('99999')))
filter函数:(过滤序列,参数:函数,序列)
Python内建的filter()函数用于过滤序列。
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
def is_odd(x):
return x % 2 != 1 # 偶数
def is_empty(x):
return x and x.strip()
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
print(list(filter(is_odd, L)))
print(list(filter(is_empty, ['A', '', 'B', None, 'C', ' '])))
filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list(和map一样)
sorted函数:(排序算法,参数:序列,key=函数)
Python内置的sorted()函数就可以对list进行排序
print(sorted([5, 2, 3, 4, 6])) # [2, 3, 4, 5, 6]
print(sorted([-5, 2, 3, 4, -6])) # [-6, -5, 2, 3, 4]
print(sorted([-5, 2, 3, 4, -6], key=abs)) # [2, 3, 4, -5, -6]
print(sorted(['bob', 'about', 'Zoo', 'Credit'])) # ['Credit', 'Zoo', 'about', 'bob']
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)) # ['about', 'bob', 'Credit', 'Zoo']
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.upper)) # ['about', 'bob', 'Credit', 'Zoo']
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.upper, reverse=True)) # 逆序排序 ['Zoo', 'Credit', 'bob', 'about']
def by_name(t): # 按姓名排序
return t[0]
def by_score(t): # 按分数排序
return -t[1]
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
print(sorted(L, key=by_name))
print(sorted(L, key=by_score))
返回函数:
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
def lazy_sum(*x):
def sum():
retsum = 0
for i in x:
retsum = retsum + i
return retsum
return sum # 返回sum函数
print(lazy_sum(1, 2, 3, 4, 5, 6, 7, 8)) # <function lazy_sum.<locals>.sum at 0x1058f9200>
# 因为lazy_sum返回的是一个函数,因此需要调用才可以打印出结果
print(lazy_sum(1, 2, 3, 4, 5, 6, 7, 8)()) # 36
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
传入到counter中的变量不能改变,我们只需要传一个不会变的值进去即可.而这个值不一定是常规的变量
比如下面的cnt设置成0的话,在counter函数中,我们并不能改变cnt的值,但是如果我们传的cnt是一个列表的话,其实本质上传递的是一个地址,在这个地址上我们可以做任意改变.只要这个列表还是这个列表,他的地址就不会变.利用这种特性,我们就可以随便折腾了
匿名函数:(lambda)
有些时候,不需要显式地定义函数,直接传入匿名函数更方便
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数lambda x: x * x
print(list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8]))) # 求x的平方
print(list(filter(lambda x: x % 2 == 1, range(1, 20)))) # 筛选奇数
关键字lambda表示匿名函数,冒号前面的x表示函数参数
装饰器:
函数对象有一个__ name __ 属性,可以拿到函数的名字now.__ name __ = ’now’
假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
def log(func):
def wrapper(*args, **kw):
print('call %s' % func.__name__)
return func(*args, **kw)
return wrapper
# 把@log放到now()函数的定义处,相当于执行了语句now = log(now)
# wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用
# 在wrapper()函数内,首先打印日志,再紧接着调用原始函数
@log
def now():
print('2020-6-28,12:00')
print(now.__name__) # wrapper(name已经发生了改变了)
now() # 2020-6-28,12:00
def log(test):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (test, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute’) # 可以输入指定的文本
def now():
print('2020-6-28,12:00')
print(now.__name__) # wrapper(name已经发生了改变了)
now()
因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错
不需要编写wrapper.name = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的
定义wrapper()的前面加上@functools.wraps(func)即可
import functools
def log(func):
@functools.wraps(func) # wrapper.__name__ = func.__name__
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
import functools
def log(test):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
return decorator
# 设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间
def metric(func):
@functools.wraps(func)
def wrapper(*args, **kw):
t = time.time() # 现在的时间
func(*args, **kw) # 运行一下函数
print('%s executed in %s ms' % (func.__name__, time.time() - t)) # 计算时间差
return func(*args, **kw)
return wrapper
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z
f = fast(11, 22)
s = slow(11, 22, 33)
偏函数:
通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。
print(int('10', 2)) # 计算10的二进制输出
def int2(x, base=2): # 设计int2为专门输出二进制的函数
return int(x, base)
print(int2('1000’)) # 应用int2,输出8
functools.partial就是帮助我们创建一个偏函数的
int2 = functools.partial(int, base=2)
print(int2('1000’)) # 8
创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数
functools.partial(int, base=2)实际上固定了int()函数的关键字参数base相当于:
kw = { 'base': 2 }
int('10010', **kw)
max2 = functools.partial(max, 10)实际上会把10作为*args的一部分自动加到左边,也就是
max2(5, 6, 7)就相当于:
args = (10, 5, 6, 7)
max(*args) # 结果为10