简单说,“函数式编程"是一种"编程范式”(programming paradigm),也就是如何编写程序的方法论。
它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。
举例来说,现在有这样一个数学表达式:
(1+2)*3 -4
传统的过程式编程,可以写作:
a = 1+2
b = a*3
c = b-4
函数式编程就是把运算过程定义为不同的函数:
result = subtract(multiply(add(1,2),3)3)
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
高阶函数是指: 接受一个或者多个函数作为输入,输出一个函数.
python中的函数有以下特性:
变量可以指向函数:
该概念是说可以把一个函数赋值给一个变量,例如:
a = abs(-1) # 调用函数abs(-1)将计算结果赋值给a,那么a将是值为1的数字
b = abs # 将abs写个函数本身赋值给变量b,即变量b指向abs这个函数
将函数赋值给变量b后即可将b当作函数调用.
>>>b(-1)
>>>1
函数名也是变量
函数名实际上就是指向函数的变量. 对于abs()这个函数,可以abs看作一个变量,它指向一个可以计算绝对值的函数.
传入函数
既然变量可以指向函数,函数的参数能接收变量.
一个简单的示例:
f = abs
def add(x,y,f):
return f(x) + f(y)
add(-1,1,f) #结果为2
map()
map()和reduce()函数是python的内建函数(builtins)
map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的 Iterator
返回。
举例说明:
def f(x):
return x**2
r = map(f,[1,2,3]) # r = [1,3,9]
上述语句中的map函数将第二个参数中的列表元素逐个作为参数并调用函数f,将计算值组成一个新的list并返回.
reduce()
再看reduce的用法。reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f,[1,2,3,4]) = f(f(f(1,2)3)4)
上面的代码已经很清楚的表现出了reduce函数的函数,它能很简洁的表达右边的过程.
在python2中 map()和reduce()函数返回列表,在python3中返回的是一个可迭代对象.
filter
顾名思义是一个过滤器. 它也接受一个函数和一个可迭代对象.
根据函数名也可以猜测到该函数是用于过滤,它顾虑掉序列中的不符合要求的元素.
所以和map一样filter传入的第一个参数是一个函数,它返回Ture/False,用来判断序列中的元素是否满足要求.下面的filter也一样.
举例:
#过滤掉大于10的数
def f(x):
return f <= 10
a=list(filter(f,[1,11,20,8,9]) # a的值为[1,8,9]
函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
举例说明:
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
调用函数f时,才真正计算求和的结果:
>>> f()
25
闭包
闭包概念:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。
所以上述的lazy_sum()
函数中的sum()
函数就是闭包.
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
参考python官方文档
该模块提供了函数和类,以支持函数式编程风格和在可调用对象上的通用操作。
为高效循环而创建迭代器的函数
python文档
重要的函数:
无穷迭代器:
迭代器 | 实参 | 结果 | 示例 |
---|---|---|---|
count() | start, [step] | start, start+step, start+2*step, … | count(10) --> 10 11 12 13 14 … |
cycle() | p | p0, p1, … plast, p0, p1, … | cycle(‘ABCD’) --> A B C D A B C D … |
repeat() | elem [,n] | elem, elem, elem, … 重复无限次或n次 | repeat(10, 3) --> 10 10 10 |
根据最短输入序列长度停止的迭代器:
迭代器 | 实参 | 结果 | 示例 |
---|---|---|---|
accumulate() | p [,func] | p0, p0+p1, p0+p1+p2, … | accumulate([1,2,3,4,5]) --> 1 3 6 10 15 |
chain() | p, q, … | p0, p1, … plast, q0, q1, … | chain(‘ABC’, ‘DEF’) --> A B C D E F |
chain.from_iterable() | iterable – 可迭代对象 | p0, p1, … plast, q0, q1, … | chain.from_iterable([‘ABC’, ‘DEF’]) --> A B C D E F |
compress() | data, selectors | (d[0] if s[0]), (d[1] if s[1]), … | compress(‘ABCDEF’, [1,0,1,0,1,1]) --> A C E F |
dropwhile() | pred, seq | seq[n], seq[n+1], … 从pred首次真值测试失败开始 | dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1 |
filterfalse() | pred, seq | seq中pred(x)为假值的元素,x是seq中的元素。 | filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8 |
groupby() | iterable[, key] | 根据key(v)值分组的迭代器 | |
islice() | seq, [start,] stop [, step] | seq[start:stop:step]中的元素 | islice(‘ABCDEFG’, 2, None) --> C D E F G |
starmap() | func, seq | func(*seq[0]), func(*seq[1]), … | starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000 |
takewhile() | pred, seq | seq[0], seq[1], …, 直到pred真值测试失败 | takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4 |
tee() | it, n | it1, it2, … itn 将一个迭代器拆分为n个迭代器 | |
zip_longest() | p, q, … | (p[0], q[0]), (p[1], q[1]), … | zip_longest(‘ABCD’, ‘xy’, fillvalue=’-’) --> Ax By C- D- |
排列组合迭代器:
迭代器 | 实参 | 结果 |
---|---|---|
product() | p, q, … [repeat=1] | 笛卡尔积,相当于嵌套的for循环 |
permutations() | p[, r] | 长度r元组,所有可能的排列,无重复元素 |
combinations() | p, r | 长度r元组,有序,无重复元素 |
combinations_with_replacement() | p, r | 长度r元组,有序,元素可重复 |
product(‘ABCD’, repeat=2) | AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD | |
permutations(‘ABCD’, 2) | AB AC AD BA BC BD CA CB CD DA DB DC | |
combinations(‘ABCD’, 2) | AB AC AD BC BD CD | |
combinations_with_replacement(‘ABCD’, 2) | AA AB AC AD BB BC BD CC CD DD |
operator 模块提供了一套与Python的内置运算符对应的高效率函数。例如,operator.add(x, y) 与表达式 x+y 相同。 许多函数名与特殊方法名相同,只是没有双下划线。为了向后兼容性,也保留了许多包含双下划线的函数。为了表述清楚,建议使用没有双下划线的函数。
函数包含的种类有:对象的比较运算、逻辑运算、数学运算以及序列运算。
对象比较函数适用于所有的对象,函数名根据它们对应的比较运算符命名。
官方文档
运算 | 语法 | 函数 |
---|---|---|
加法 | a + b | add(a, b) |
字符串拼接 | seq1 + seq2 | concat(seq1, seq2) |
包含测试 | obj in seq | contains(seq, obj) |
除法 | a / b | truediv(a, b) |
除法 | a // b | floordiv(a, b) |
按位与 | a & b | and_(a, b) |
按位异或 | a ^ b | xor(a, b) |
按位取反 | ~ a | invert(a) |
按位或 | a | b | or_(a, b) |
取幂 | a ** b | pow(a, b) |
一致 | a is b | is_(a, b) |
一致 | a is not b | is_not(a, b) |
索引赋值 | obj[k] = v | setitem(obj, k, v) |
索引删除 | del obj[k] | delitem(obj, k) |
索引取值 | obj[k] | getitem(obj, k) |
左移 | a << b | lshift(a, b) |
取模 | a % b | mod(a, b) |
乘法 | a * b | mul(a, b) |
矩阵乘法 | a @ b | matmul(a, b) |
否定(算术) | - a | neg(a) |
否定(逻辑) | not a | not_(a) |
正数 | + a | pos(a) |
右移 | a >> b | rshift(a, b) |
切片赋值 | seq[i:j] = values | setitem(seq, slice(i, j), values) |
切片删除 | del seq[i:j] | delitem(seq, slice(i, j)) |
切片取值 | seq[i:j] | getitem(seq, slice(i, j)) |
字符串格式化 | s % obj | mod(s, obj) |
减法 | a - b | sub(a, b) |
真值测试 | obj | truth(obj) |
比较 | a < b | lt(a, b) |
比较 | a <= b | le(a, b) |
相等 | a == b | eq(a, b) |
不等 | a != b | ne(a, b) |
比较 | a >= b | ge(a, b) |
比较 | a > b | gt(a, b) |
函数 | 功能 |
---|---|
functools.cmp_to_key(func) | 将cmp函数转换为key函数,为了兼容py2 |
@functools.lru_cache(maxsize=128, typed=False) | 一个为函数提供缓存功能的装饰器,缓存 maxsize 组传入参数,在下次以相同参数调用时直接返回上一次的结果。用以节约高开销或I/O函数的调用时间。有了它就可以不使用dp啦。 |
@functools.total_ordering | 它是针对某个类如果定义了lt、le、gt、ge这些方法中的至少一个,使用该装饰器,则会自动的把其他几个比较函数也实现在该类中。此类必须包含以下方法之一:lt() 、le()、gt() 或 ge()。另外,此类必须支持 eq() 方法。 |
functools.partial(func, *args, **keywords) | 该函数的目的是为了减少参数func函数的参数。返回一个于func功能一样的函数,但是预先填写了参数(partial函数后两个参数传入的。) |
class functools.partialmethod(func, *args, **keywords) | 返回一个新的partialmethod描述符,该描述符的行为类似于partial,只是它被设计为用作方法定义而不是可直接调用的。 |
functools.reduce(function, iterable[, initializer]) | reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) = ((((1+2)+3)+4)+5). |
partial对象是由partial()函数创建的可调用对象. 它有三个只读属性:
functools.partial(func, *args, **keywords)
parial.dunc
一个可调用对象或者函数. 调用partial对象将会带着传入的参数转发给func.
也就是说调用partial实际上是调用func的功能只是将partial的参数传递给func
partial.args
调用partial时传入的参数,它将会被传递给func
partial.keywords
调用partial时传入的关键字参数. 它将被传入func
参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017328525009056
参考:https://docs.python.org/zh-cn/3.7/library/functional.html