python精简笔记(五)——函数式编程
来源:互联网 发布:星星知我心演员表 编辑:程序博客网 时间:2024/05/20 07:52
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
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==f2False
f1()
和f2()
的调用结果互不影响。
闭包
返回的函数在其定义内部引用了局部变量 args
,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用。
闭包用起来简单,实现起来可不容易。
错误示范
def count():fs = []for i in range(1, 4):def f():return i*ifs.append(f)return fsf1, 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
置于函数的定义处:
@logdef 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 functoolsdef 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精简笔记(五)——函数式编程
- Python学习笔记(五)——函数式编程
- python精简笔记(四)——高阶函数
- python3精简笔记(二)——函数
- Python学习笔记——函数式编程
- 廖雪峰python学习笔记——函数式编程
- Python函数式编程——学习笔记
- Python学习笔记——函数式编程
- 【Python】学习笔记——-5.0、函数式编程
- 函数式编程——Python学习笔记05
- Python笔记(二)——高级特性|函数式编程
- Python学习笔记——函数式编程
- Python笔记--函数式编程
- python精简笔记(六)——内置模块介绍
- 【python学习笔记】Python函数式编程
- 《Python编程》笔记(五)
- Python笔记4——函数式编程,高阶函数,闭包
- Python进阶学习笔记——函数式编程之高阶函数
- makefile和shell脚本
- springmvc基础一
- 使用七牛的sdk上传报错:incorrect region
- 【js初学】whlie语句
- 分页+frament+PullToRefresh+轮播
- python精简笔记(五)——函数式编程
- 数据结构与算法分析:Java语言描述 mobi格式
- 关于敏捷入门的一本好书推荐
- docker使用GDB,无法进入断点的解决方法
- 第二章 数据类型
- 显示服务器配置信息
- Servlet API 中文版
- PullToRefresh与listView联用
- 关于Android引用出错