https://www.liaoxuefeng.com/wiki/1016959663602400/1017328525009056(学习内容来自:廖雪峰的网站)
函数式编程
Functional Programming,思想更接近数学计算,是一种抽象程度很高的编程范式
允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
高阶函数
Higher-order function
函数本身也可以赋值给变量,即变量可以指向函数,函数名也是变量,函数名其实就是指向函数的变量,
map/reduce
- map
map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
r是Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
# 把这个list所有数字转为字符串
-
reduce
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,接收两个参数,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
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
把序列[1, 3, 5, 7, 9]变换成整数13579
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
str转换为int
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> def char2num(s):
... digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
... return digits[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579
filter
filter()
接收一个函数和一个序列,函数依次作用于每个元素,根据返回值是True
还是False
决定保留还是丢弃该元素。
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
注意到filter()
函数返回的是一个Iterator
,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list
求素数-埃氏筛法
构造一个从3开始的奇数序列
def _odd_iter():
n=1
while True:
n=n+2
yield n
#一个生成器,并且是一个无限序列
筛选函数
def _not_divisible(n):
return lambda x: x % n > 0
定义一个生成器,不断返回下一个素数
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
# 打印1000以内的素数:
for n in primes():
if n < 1000:
print(n)
else:
break
我在学习时lambda没太看懂,写了一个粗糙的易懂版本:
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
def _not_divisible(n):
for i in range(2,n):
if n%i>0:
continue
else:
return False
return True
#return x % n == 0
it = _odd_iter()
for i in range(30):
it = filter(_not_divisible, it) # 构造新序列
print(next(it))
函数作为返回值
当不需要立刻求和
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
#调用lazy_sum()时,返回的并不是求和结果,而是求和函数
闭包
注意:
- 当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
- 返回的函数并没有立刻执行,而是直到调用了f()才执行
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
匿名函数
关键字lambda
表示匿名函数,冒号前面的x
表示函数参数。匿名函数只能有一个表达式,不用写return
,返回值就是该表达式的结果。
匿名函数lambda x: x * x
实际上是
def f(x):
return x * x
好处:
- 因为函数没有名字,不必担心函数名冲突。
- 匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
- 把匿名函数作为返回值返回
>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25
-----------------------------------
def build(x, y):
return lambda: x * x + y * y
-----------------------------------
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
#上下两式子输出相同
L = list(filter(lambda x:x%2==1, range(1, 20)))
装饰器
>>> def now():
... print('2015-3-25')
函数对象有一个__name__
属性,可以拿到函数的名字
假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为**“装饰器”(Decorator)**
本质上,decorator就是一个返回函数的高阶函数
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
log
是一个decorator
,接受一个函数作为参数,并返回一个函数
借助Python的@语法
,把decorator置于函数的定义处
@log
def now():
print('2015-3-25')
调用now()
函数,不仅会运行now()
函数本身,还会在运行now()
函数前打印一行日志
>>> now()
call now():
2015-3-25
把@log
放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
偏函数(Partial function)
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2
def int2(x, base=2):
return int(x, base)
#代替:
>>> import functools
>>> int2 = functools.partial(int, base=2)
#同
kw = { 'base': 2 }
int('10010', **kw)
functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数
当传入:
max2 = functools.partial(max, 10)
实际上会把10作为*args的一部分自动加到左边,也就是:
max2(5, 6, 7)
#相当于:
args = (10, 5, 6, 7)
max(*args)#结果为10