之前有段时间看了一下函数式相关的东西,Python本身内建的一些函数对于代码的处理效率有很大的提升。所以采用比如map/reduce等函数式的编程方法能更好的把代码从for...in等遍历中解放出来,可读性也是有点帮助的。
函数式(Functional Programming )
Python
介绍
函数式,允许把函数本身作为参数传入另一个函数,还允许返回一个函数
纯粹的函数式编写的函数没有变量,所以输入确定,输出就是确定的
Python对函数式提供部分支持。允许使用变量,所以Python不是纯函数式编程语言
高阶函数
Higher-order function
①变量可以指向函数:
>>> abs(-10)
10
>>> f = abs
>>> f(-1)
1
>>> abs
<built-in function abs>
>>> f
<built-in function abs>
>>>
abs()是调用函数,abs是函数本身。f可以实现abs功能,所以f已经指向了abs本身。
②函数名也是变量
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
File "<pyshell#130>", line 1, in <module>
abs(-10)
TypeError: 'int' object is not callable
>>> abs
10
>>>
可见函数abs已经从指向一个求绝对值的函数指向了一个整数10,即函数名也是变量
③传入函数
>>> f = abs
>>> def add(x,y,f):
return f(x)/f(y)
>>> add(-5,5,f)
1
>>>
把函数作为参数传入,这种函数称为高阶函数
map()/reduce()
map(function, iterable)
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到每个元素,并把结果作为新的Iterable(list)返回。
>>> def f(x):
return x*x
>>> L = [1,2,3,4]
>>> map(f, L)
[1, 4, 9, 16]
>>>
reduce(function, iterable[, initializer])
把一个函数作用在一个序列上,reduce继续把结果和下一个元素做累积计算。
>>> reduce(lambda x,y:x+y, [1,2,3,4,5])
15
>>>
实际计算((((1+2)+3)+4)+5)
initializer参数:
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
try:
initializer = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = initializer
for x in it:
accum_value = function(accum_value, x)
return accum_value
filter()
filter(function, Iterable)
filter把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
>>> def is_odd(n):
return n%2==1
>>> list(filter(is_odd, [1,2,3,4,5,6,7]))
[1, 3, 5, 7]
>>>
计算素数:埃拉托色尼筛选法
sorted()
对list排序:
>>> sorted([1,2,4,3,7,65,4,4,-3,-5])
[-5, -3, 1, 2, 3, 4, 4, 4, 7, 65]
接收key函数实现自定义排序:
>>> sorted([1,2,4,3,7,65,4,4,-3,-5], key=abs)
[1, 2, 3, -3, 4, 4, 4, -5, 7, 65]
反向排序,可以传入第三个参数reverse=True:
>>> sorted([1,2,4,3,7,65,4,4,-3,-5], key=abs, reverse=True)
[65, 7, -5, 4, 4, 4, 3, -3, 2, 1]
返回函数
①函数可作为返回值
>>> def calc_sum(*args):
def sum():
start_num = 0
for n in args:
num = start_num + n
return num
return sum
>>>
>>> calc_sum(1,2,34,5)
<function sum at 0x032DD330>
返回的是function而不是计算结果。
我们在函数calc_sum中定义了函数sum,且内部函数sum可以引用外部函数calc_sum的参数和局部变量,当calc_sum返回函数sum时,相关参数和变量都保存在返回的函数中,称之为闭包。
>>> f1 = calc_sum(1,2)
>>> f2 = calc_sum(1,2)
>>> f1
<function sum at 0x032DAF70>
>>> f2
<function sum at 0x032DD3F0>
*每次调用calc_sum返回一个新的函数。
*返回函数没有立即执行,直到调用才执行。
>>> def count():
fs = []
for i in range(1,4):
def f():
return i*i
fs.append(f)
return fs
>>> f = count()
>>> f
[<function f at 0x032DD3F0>, <function f at 0x032DDF30>, <function f at 0x032DDF70>]
>>> for i in f:
print i
<function f at 0x032DD3F0>
<function f at 0x032DDF30>
<function f at 0x032DDF70>
>>> for i in f:
print i()
9
9
9
上面函数引用变量i,不是立即执行,是等到3个函数都返回才执行,此时引用变量i已经变成了3.
闭包时:返回函数不要引入循环变量,或者后续发生变化的变量。
必须引入时,可以在创建一个函数,用该函数的参数绑定循环变量的当前值,无论改循环变量后续如何更改,已绑定到函数的参数值不变。
>>> def count():
fs = []
def f(j):
def g():
return j*j
return g
for i in range(1,4):
fs.append(f(i))
return fs
>>>
>>> f = count()
>>> f
[<function g at 0x032EA030>, <function g at 0x032EA070>, <function g at 0x032EA0B0>]
>>> for num in f:
print num()
1
4
9
>>>
匿名函数
关键字:lambda
语法:lambda x, y: x+y
冒号前面x,y表示参数,冒号后面的x+y表示执行方式
匿名函数是一个函数对象可以赋值给一个变量
可以把匿名函数作为返回值返回
装饰器
代码运行期间动态增加功能的方式,称为装饰器(Decorator)
本质上decorator还剩一个返回函数的高阶函数。
eg:
一个基础函数now函数:
>>> def now():
print('2016')
>>> f = now()
2016
>>> f1 = now
>>> f1()
2016
函数对象的__name__属性可以拿到函数的名字
>>> f1.__name__
'now'
>>>
现在增强函数now的功能,在函数调用前自动打印日志,但又不希望修改now函数的定义。所以使用decorator。
>>> def log(func):
def wrapper(*args, **kw):
print('call %s():' %func.__name__)
return func(*args, **kw)
return wrapper
>>>
>>> @log
def now():
print('2016')
>>>
>>> now()
call now():
2016
把@log放在now函数的定义处,相当于执行了:
now = log(now)
原先的now函数依旧存在,现在的now函数指向了装饰器log返回的一个新的函数,执行的是这个新的函数。
wrapper函数参数定义是(*args, **kw),所以wrapper可以接受任意参数的调用。在wrapper内先打印日志再调用原函数。
如果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
>>>
>>> @log('hehe')
def now():
print('time')
>>> now()
hehe now():
time
>>>
三层嵌套的decorator的效果是:
now = log('hehe')(now)
首先执行log('hehe'),返回的是decorator函数,在调用返回的函数,参数是now函数,返回值最终是wrapper函数。
上述decorator定义没有问题,但是函数也是对象,经过decorator装饰的函数的__name__等属性改变。
从原来的now变为wrapper。
>>> now.__name__
'wrapper'
返回的wrapper()的函数名就是wrapper,需要把原始函数的__name__等属性复制到wrapper函数中,否则有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__这样的代码,
Python提供functools.wraps提供这样的方法。
完整的decorator写法如下:
>>> import functools
>>> def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s()' %func.__name__)
return func(*args, **kw)
return wrapper
>>> @log
def now():
print('time')
>>> now()
call now()
time
>>> now.__name__
'now'
>>>
针对带参数的decorator:
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
定义wrapper函数前加:@functools.wraps(func)
@luoluoying
2016-09-20 15:37
字数 4340
阅读 0