python精简笔记(五)——函数式编程
2017-09-21 09:54
190 查看
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
调用函数f时,才真正计算求和的结果:
上面这种形式称为闭包。
调用
闭包用起来简单,实现起来可不容易。
错误示范
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
匿名函数
关键字
匿名函数有个限制,就是只能有一个表达式,不用写
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
同样,也可以把匿名函数作为返回值返回,比如:
函数对象有一个
假设我们要增强
观察上面的
调用
把
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
这个3层嵌套的decorator用法如下:
相当于
装饰器会改变 函数
Python内置的
例如
要转换大量的二进制字符串,每次都传入
简化代码
创建偏函数时,实际上可以接收函数对象、
相当于
当传入:
实际上会把10作为*args的一部分自动加到左边,也就是:
相当于:
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
返回函数
函数可以把函数作为返回值返回,如果不需要立刻求和,而是在后面的代码中,以不返回求和的结果,而是返回求和的函数: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()时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1==f2 False
f1()和
f2()的调用结果互不影响。
闭包
返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用。
闭包用起来简单,实现起来可不容易。
错误示范
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
>>> f1() 9 >>> f2() 9 >>> f3() 9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f() return fs
匿名函数 lambda
传入函数时,有些时候,直接传入匿名函数更方便。匿名函数
lambda x: x * x实际上就是:
def f(x): return x * x
关键字
lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写
return,返回值就是该表达式的结果。
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
>>> f = lambda x: x * x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25
同样,也可以把匿名函数作为返回值返回,比如:
def build(x, y): return lambda: x * x + y * y
装饰器
函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。>>> def now(): ... print('2015-3-25') ... >>> f = now >>> f() 2015-3-25
函数对象有一个
__name__属性,可以拿到函数的名字:
>>> now.__name__ 'now' >>> f.__name__ 'now'
假设我们要增强
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)
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
这个3层嵌套的decorator用法如下:
@log('execute') def now(): print('2015-3-25')
相当于
now = log('execute')(now)
装饰器会改变 函数
__name__属性
Python内置的
functools.wraps可以解决这个问题,只需记住在定义
wrapper()的前面加上
@functools.wraps(func)即可。
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
偏函数
当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
例如
int()函数可以把字符串转换为整数,当仅传入字符串时,
int()函数默认按十进制转换,如果传入base参数,就可以做N进制的转换:
>>> int('12345', base=8) 5349 >>> int('12345', 16) 74565
要转换大量的二进制字符串,每次都传入
int(x, base=2)非常麻烦
简化代码
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
创建偏函数时,实际上可以接收函数对象、
*args和
**kw这3个参数
int2 = functools.partial(int, base=2) int2('10010')
相当于
kw = { 'base': 2 } int('10010', **kw)
当传入:
max2 = functools.partial(max, 10)
实际上会把10作为*args的一部分自动加到左边,也就是:
max2(5, 6, 7)
相当于:
args = (10, 5, 6, 7) max(*args)
相关文章推荐
- Python笔记4——函数式编程,高阶函数,闭包
- Python自学笔记之函数式编程3——filter
- Python学习笔记——函数式编程
- 【Python】学习笔记——-5.0、函数式编程
- 【Python学习笔记】函数式编程
- 【Python学习笔记】函数式编程:高阶函数filter
- python 学习笔记---函数式编程之高阶函数
- Python进阶学习笔记——函数式编程之高阶函数
- Python学习笔记,函数式编程,高阶函数
- 【Python学习笔记】函数式编程:高阶函数
- 【Python学习笔记】函数式编程:高阶函数sorted
- Python笔记(二)——高级特性|函数式编程
- Python学习笔记11:Python函数和函数式编程
- python学习笔记011——函数式编程
- 廖雪峰python学习笔记——函数式编程
- Python学习笔记4--函数式编程
- 【python学习笔记】函数式编程:返回函数
- 廖雪峰Python教程学习笔记4-函数式编程
- python学习笔记-函数式编程、迭代器和生成器
- python笔记11 - lambda函数,globals()/locals()函数,eval()exec()函数,闭包函数,函数式编程,高阶函数