Python之高阶函数

时间:2023-01-30 13:30:57

参考原文

  廖雪峰Python函数式编程

什么是高阶函数(Higher-order function)?

变量可以指向函数

>>> f = abs
>>> f
<built-in function abs>
>>> f(-10)
10

函数名也是变量

  函数名是什么呢?函数名其实就是指向函数的变量。对于abs()这个函数,完全可以这样看:函数名abs是一个变量,它指向一个可以计算绝对值的函数。如果把abs指向整型数据类型,再来调用会发生什么呢?

>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
  File "<pyshell#92>", line 1, in <module>
    abs(-10)
TypeError: 'int' object is not callable

  因为abs这个变量已经不指向求绝对值的函数而是指向一个整数10了!

传入函数

  既然变量可以指向函数,函数的参数能接受变量,那么一个函数就可以接受另一个函数作为参数,这种能接受函数作为参数的函数,就叫高阶函数。一个简单的高阶函数:

# -*- conding:utf-8 -*-
def add(x, y, f):
    return f(x) + f(y)

print(add(-5, 6, abs)) # result 11
Tips:把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

map

  map函数接受2个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到Iterable序列的每个元素,并把结果作为Iterator返回。

问题:已知函数f(x)=x 2,使将该函数作用在一个list[1, 2, 3, 4, 5, 6, 7, 8, 9]上。

  在之前,你可能会用循环这么做:

def f(x):
    return x * x

L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    L.append(f(n))
print(L) #result  [1, 4, 9, 16, 25, 36, 49, 64, 81]

  但是从上面的循环代码来看,不太能一眼看明白"把f(x)作用在list的每一个元素上并把结果生成一个新的list"。现在,我们应该用map试一试:

def f(x):
    return x * x

r = map(f,[1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r)) #result [1, 4, 9, 16, 25, 36, 49, 64, 81]

  这样似乎是要简单点的,map()传入的第一个参数时f,即函数对象本身。由于得到的结果r是一个Iterator惰性序列,因此通过list()函数让它把整个序列计算出来并返回出一个list对象。

  map()作为高阶函数,它把运算规则抽象了,我们可以计算任意复杂的函数,比如把这个list所有数字转为字符串:

>>> list(map(str, [1, 2, 3, 4]))
['1', '2', '3', '4']

reduce

  reduce函数也接受2个参数,一个是函数,一个是Iterable,和map不同的是,这个函数必须接受2个参数,reduce强调把结果继续和序列的下一个元素做累积计算,返回累积后具体的数据,其效果如下:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

  可以用于对一个序列求和:

from functools import reduce
def add(x, y):
    return x + y

print(reduce(add, [1, 3, 5, 7, 9])) #result 25

  如果要把序列[1, 3, 5, 7, 9]变成整数13579,那么reduce就可以派上用场了:

from functools import reduce
def fn(x, y):
    return x * 10 + y

print(reduce(fn, [1, 3, 5, 7, 9])) #resuolt 13579

  考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们可以写成把str转换成int的函数:

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))

print(str2int('12345')) #result 整数12345

filter

  Python内建的filter()函数用于过滤序列,和map()类似,filter()也接受一个函数和一个序列,和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素,返回的是一个Iterator

  例如,在一个list中,删掉偶数,只保留奇数:

>>> list(filter(lambda x: x % 2 == 1, [1, 2, 4, 5, 6]))
[1, 5]

把一个序列中的空字符串删除:

>>> list(filter(lambda s: s and s.strip(), ['A', '', 'B', None, 'C', '  ']))
['A', 'B', 'C']
Tips:正确地使用filter函数,关键在于正确地实现一个"筛选"函数。由于filter使用了惰性序列计算,所以只有在取filter()结果的时候,才会真正筛选并返回下一个筛选出的元素哦!

sorted

  排序在程序中也是经常用到的算法,无论是冒泡排序还快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或是2个dict呢?直接比较数学上的大小是没有意义的,因此比较的过程必须通过函数抽象出来。

  为此Python内置了sorted()函数,它可以对list进行排序:

>>> sorted([3, 4, 1, 2, -1])
[-1, 1, 2, 3, 4]

  但同时,sorted()也是一个高阶函数,它还可以接受一个key函数来实现自定义的排序,如按绝对值大小的排序:

>>> sorted([3, 4, 5, 2, -1], key=abs)
[-1, 2, 3, 4, 5]

  key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序

Tips:soretd()也是一个高阶函数,用sorted()进行排序的关键在于实现一个映射函数