一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。 简单示例:
def add(x, y, f):
return f(x) + f(y)
print(add(-6, 4, abs))
map/reduce
map()函数接收两个参数,一个是函数,一个是Iterable,map()将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
例如有一个函数:
f(x)=x2
要把这个函数作用在一个list[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:
def f(x):
return x * x
L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
r = map(f, L)
print(list(r))
输出结果:[1, 4, 9, 16, 25, 36, 49, 64, 81]
使用map()函数把list的所有整数转换为字符串:
print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
输出结果:['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce()函数就是把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
用reduce()把序列[1, 3, 5, 7, 9]变换成整数13579:
from functools import reduce
def fn(x, y):
return x * 10 + y
L = [1, 3, 5, 7, 9]
print(reduce(fn, L))
结果:13579
filter
filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如删掉一个序列中的偶数,保留奇数:
def is_odd(n):
return n % 2 == 1
L = [1, 2, 4, 5, 6, 9, 10, 15]
print(list(filter(is_odd, L)))
输出结果:[1, 5, 9, 15]
删除一个列表里面的空字符串:
def not_empty(s):
return s and s.strip()
L = filter(not_empty, ['A', '', 'B', None, 'C', ' '])
print(list(L))
输出结果:['A', 'B', 'C']
filter()返回的是原来的元素,filter()的第一个函数只能起到一个判断的作用。
sorted
Python内置的sorted()函数就可以对list进行排序:
print(sorted([36, 5, -12, 9, -21]))
输出结果:[-21, -12, 5, 9, 36]
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
print(sorted([36, 5, -12, 9, -21], key=abs))
输出结果:[5, 9, -12, -21, 36]
字符串排序:
print(sorted(['bob', 'about', 'Zoo', 'Credit']))
输出结果:['Credit', 'Zoo', 'about', 'bob']
对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
给sorted()传入key函数,即可实现忽略大小写的排序:
L = ['bob', 'about', 'Zoo', 'Credit']
print(sorted(L, key=str.lower))
输出结果:['about', 'bob', 'Credit', 'Zoo']
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:
L = ['bob', 'about', 'Zoo', 'Credit']
print(sorted(L, key=str.lower, reverse=True))
输出结果:['Zoo', 'Credit', 'bob', 'about']
返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。返回函数可以把一些计算延迟执行。例如,如果定义一个普通的求和函数:
def out_sum(lst):
#定义函数 in_sum
def in_sum():
return sum(lst)
#返回函数 in_sum
return in_sum
下面代码并没有对函数进行执行计算出结果,而是返回函数:
L = [1, 2, 3, 4, 5]
print(out_sum(L))
输出结果:.in_sum at 0x00000179D74F0400>
对返回的函数进行调用时,才计算出结果:
L = [1, 2, 3, 4, 5]
f = out_sum(L)
#函数名后加(),才是调用该函数。
print(f())
#结果 15
上面的例子中,函数out_sum中又的定义了函数in_sum,内部函数in_sum引用外部函数的参数和局部变量,当外部函数out_sum返回in_sum时,参数和变量保存在返回函数中,这种程序结构成为:闭包(Closure)。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
当调用out_sum时候,每次返回的都是一个新的函数:
L = [1, 2, 3, 4, 5]
f1 = out_sum(L)
f2 = out_sum(L)
if f1 == f2:
print('YES')
else:
print('NO')
#结果 NO
#函数f1() 和 f2() 结果互不影响。
一个函数可以返回一个计算结果,也可以返回一个函数。返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
匿名函数
当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。
L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list(map(lambda x: x * x, L)))
输出结果:[1, 4, 9, 16, 25, 36, 49, 64, 81]
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
#匿名函数:
lambda x: x * x
#实际就是:
def f(x):
return x * x
匿名函数只能有一个表达式,不用写return,返回值就是该表达式的结果。
匿名函数可以赋值给一个变量,再用变量调用这个函数:
f = lambda x: x * x
print(f)
#结果 at 0x000001DFDAF91E18>
print(f(5))
结果 25
装饰器
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
装饰器本质上是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限验证等场景,装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
示例:
import datetime
import time
import functools
def log(func):
# 避免经过装饰之后的原本函数名变成了 wrapper ,需要在 wrapper 前加入:
@functools.wraps(func)
# 接收add传参
def wrapper(*args, **kw):
# 参数解构,返回值赋给ret
ret = func(*args, **kw)
# 打印当前时间
print('当前时间:', datetime.datetime.now(), '\r=======')
# 返回 add 函数执行结果,并加以装饰
return('函数名:{}()\r参数:{}\r结算结果:{}'.format(func.__name__, args, ret))
return wrapper
# @log 等于 add = log(add) 也就是 wrapper()
@log
def add(*args):
j = 0
for i in args:
j += i
return j
t = (1, 2, 3, 4, 5)
print(add(*t))
#验证打印原函数名是否被修改
print('原函数名为:', add.__name__)
定义一个带参数的装饰器:
# -*- coding: utf-8 -*-
import functools
import datetime
def hello():
return 'hello!'
# 打印时间
def out_time():
print('当前时间:', datetime.datetime.now(), '\r=======')
# 定义一个带参数的装饰器
def log(f1, f2, f3):
# 传入add作为参数
def decorator(func):
# 避免经过装饰之后的原本函数名变成了 wrapper ,需要在 wrapper 前加入:
@functools.wraps(func)
# 接收add的传参
def wrapper(*args):
f2()
ret = func(*args)
print('{} {}'.format(f1(), f3))
print('使用函数为:{}() 参数为:{}'.format(func.__name__, args))
return '计算结果为:{}'.format(ret)
# 返回函数wrapper
return wrapper
# 返回函数decorator
return decorator
# 执行带参装饰器,@log 等价于 add = log(hello, out_time, '奔跑')(add)
@log(hello, out_time, '奔跑')
#定义一个垒加函数,形参为可变参数
def add(*args):
j = 0
for i in args:
j += i
return j
L = [1, 2, 3, 4]
#传入实参,解构
print(add(*L))
#验证原函数名是否被修改
print(add.__name__)
偏函数
Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。
用functools.partial创建一个偏函数:
import functools
int2 = functools.partial(int, base=2)
print(int2('1010'))
#相当于:
print(int('1010', base=2))
简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(默认传入原函数),返回一个新的函数。
也可以在调用新函数时候传入其他参数:
print(int2('1010', base=10))
创建偏函数时,实际上可以接收函数对象、args和*kw这3个参数,当传入:
max2 = functools.partial(max, 10)
实际上会把10默认为*args的一部分自动加到左边,也就是:
max2(5, 6, 7)
相当于:
args = (10, 5, 6, 7)
max(*args)
结果为10。
当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。