1 函数式编程概述
前提:函数在 Python 中是⼀等对象
工具:built-in ⾼阶函数;lambda 函数;operator 模块;functools 模块
模式:闭包与装饰器
替代:⽤用 List Comprehension 可轻松替代 map 和 filter(reduce 替代起来⽐比较困难)
原则:No Side Effect
何为 No Side Effect? 函数的全部功能就仅仅是返回一个新的值而已,没有其余行为,尤为是不得修改外部变量。因⽽,各个独⽴的部分的执⾏顺序能够随意打乱,带来执⾏顺序上的⾃自使得⼀系列新的特性得以实现:⽆锁的并发;惰性求值;编译器器级别的性能优化等
1.1 程序的状态与命令式编程
程序的状态首先包含了当前定义的所有变量
有了程序的状态,咱们的程序才能不断往前推动
命令式编程,就是经过不断修改变量的值,来保存当前运⾏的状态,来步步推动
1.2 函数式编程
经过函数来保存程序的状态(经过函数建立新的参数和返回值来保存状态)
函数一层层的叠加起来,每一个函数的参数或返回值表明了⼀个中间状态
命令式编程⾥一次变量值的修改,在函数式编程⾥变成了⼀个函数的转换
最天然的方式:递归
2 一等函数
一等对象的定义:
在运⾏时建立
能赋值给变量或数据结构中的元素
能做为参数传给函数
能做为函数的返回结果
Python 中,全部函数的都是一等对象,简称为一等函数
2.1 高阶函数
定义:接受函数为参数,或把函数做为返回结果的函数
2.1.1 map 映射
map() 是 Python 内置的高阶函数,它接收一个函数 f 和一个可迭代对象,并经过把函数 f 依次做用在 可迭代对象 的每一个元素上,并返回一个新的可迭代对象。
def f(x):
return x * x
print('map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])):',
list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
show:
map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])): [1, 4, 9, 16, 25, 36, 49, 64, 81]
替代方案:
[x*x for x in range(1,10)]
def format_name(s):
s1 = s[0:1].upper() + s[1:].lower()
return s1
print("map(format_name, ['adam', 'LISA', 'barT']):",
list(map(format_name, www.mhylpt.com['adam', 'LISA', 'barT'])))
map(format_name, ['adam', 'LISA', 'barT']): ['Adam', 'Lisa', 'Bart']
替代方案:
[format_name(name) for i, name in enumerate(['adam', 'LISA', 'barT'])]
于是,列表推导能够很好的替换 map 函数。
2.1.2 filter 过滤器
x = [(), [], {}, None, '', False, 0, True, 1, 2, -3]
x_result = filter(bool,www.michenggw.com x)
list(x_result)
[True, 1, 2, -3]
替代方案:
[i for i in x if bool(i)]
print("filter((lambda x: x>0), range(-5, 5)):",
list(filter((lambda x: x > 0), range(-5, 5))))
filter((lambda x: x>0), range(-5, 5)): [1, 2, 3, 4]
替代方案:
[x for x in range(-5, 5) if x > 0]
2.1.3 reduce 递推
from functools import reduce
m = 2
n = 5
reduce(lambda x, y: x * y, list(range(1, n + 1)), m)
240
def multiply(a, b):
return a * b
reduce(multiply, range(1, 5))
24
2.1.4 zip 并行
print("zip( [1, 2, 3], [4, 5, 6]):", list(zip([1, 2, 3], [4, 5, 6])))
show:
zip( [1, 2, 3], [4, 5, 6]): [(1, 4), (2, 5), (3, 6)]
2.1.5 sorted 排序
>>> sorted([x * (-1) ** x for x in range(10)])
[-9, -7, -5, -3, -1, 0, 2, 4, 6, 8]
>>> sorted([x * (-1) ** x for x in range(10)], reverse=True)
[8, 6, 4, 2, 0, -1, -3, -5, -7, -9]
>>> sorted([x * (-1) ** x for x in range(10)], key=abs)
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
>>> sorted([x * (-1) ** x for x in range(www.mingcheng178.com)], reverse=True, key=abs)
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
min 与 max 同理。
2.2 partial
functools 这货用于高阶函数:指那些做用于函数或者返回其余函数的函数。一般状况下,只要是能够被当作函数调用的对象就是这个模块的目标。
假设有以下函数:
def multiply(x, y):
return x * y
如今,咱们想返回某个数的双倍,即:
>>> multiply(3, y=2)
6
>>> multiply(4, y=2)
8
>>> multiply(5, y=2)
10
上面的调用有点繁琐,每次都要传入 y=2,咱们想到能够定义一个新的函数,把 y=2 做为默认值,即:
def double(x, y=2):
return multiply(x, y)
如今,咱们能够这样调用了:
>>> double(www.gcyl158.com)
6
>>> double(4)
8
>>> double(5)
10
事实上,咱们能够不用本身定义 double,利用 partial,咱们能够这样:
from functools import partial
double = partial(multiply, y=2)
partial 接收函数 multiply 做为参数,固定 multiply 的参数 y=2,并返回一个新的函数给 double,这跟咱们本身定义 double 函数的效果是同样的。
因此,简单而言,partial 函数的功能就是:把一个函数的某些参数给固定住,返回一个新的函数。
须要注意的是,咱们上面是固定了 multiply 的关键字参数 y=2,若是直接使用:
double = partial(multiply, 2)
则 2 是赋给了 multiply 最左边的参数 x。
from functools import partial
def subtraction(x,www.gcyL157.com y):
return x - y
f = partial(subtraction, 4) # 4 赋给了 x
>>> f(10) # 4 - 10
-6
组合高阶函数:
from functools import partial
abs_sorted = partial(sorted, key=abs)
abs_sorted([x * (-1) ** x for x in range(10)])
show:
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
abs_reverse_sorted = partial(sorted, key=abs, reverse=True)
abs_reverse_sorted([x * (-1) ** x for x in range(10)])
show:
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
2.3 匿名函数
定义:使⽤用 lambda 表达式建立的函数,函数自己没有名字
特色:只能使⽤用纯表达式,不能赋值,不能使⽤用 while 和 try 等块语句
语法: lambda [arg1 [,arg2 [,arg3]]]: expression
Expressions get a value; Statements do something
lambda & def
写法上:
def 能够用代码块,一个代码块包含多个语句
lambda只能⽤单行表达式,⽽表达式仅仅是单个语句中的⼀种
结果上:
def 语句必定会增长⼀个函数名称
lambda 不会,这就下降了了变量名污染的⻛险
能用一个表达式直接放到 return 里返回的函数均可以⽤ lambda 速写
def multiply(a, b):
return a * b
multiply_by_lambda = lambda x,y: x * y
List + lambda 能够获得⾏为列表
f_list = [lambda x: x + 1, lambda x: x ** 2, lambda x: x ** 3]
[f_list[j](10) for j in range(3)]
[11, 100, 1000]
在 AI 领域里,这种写法经常使用于处理数据,好比按预约的⼀系列模式处理数据
下面咱们以两个例子来结束高阶函数:
例1:
L = range(6)
# 计算l中每一个元素的两倍和平方,并将两种组成一个列表
# lambda表达式和python函数同样,也能够接受函数做为参数
def twoTimes(x):
return x * 2
def square(x):
return x**2
print([list(map(lambda x: x(i), [twoTimes, square])) for i in L])
print(list(filter(lambda x: x % 2 == 0, L)))
# 内置reduce函数,计算 L 的和
print(reduce(lambda accumValue, newValue: accumValue + newValue, L, 0))
[[0, 0], [2, 1], [4, 4], [6, 9], [8, 16], [10, 25]]
[0, 2, 4]
15
咱们依然可使用列表解析的方式替换 map & filter:
[[twoTimes(x), square(x)] for x in L]
[x for x in L if x % 2 == 0]
经过上面的例子咱们发现,使用列表推导要比 map 与 filter 简洁且易于理解得多。
可是,咱们这里还有一个惰性计算的坑:
f_list = [lambda x:x**i for i in range(5)]
[f_list[j](3) for j in range(5)]
[81, 81, 81, 81, 81]
你们能够思考为何会出现这个意想不到的结果?python