Python函数式编程

       之前有段时间看了一下函数式相关的东西,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)
  2016-09-20 15:37  字数 4340  阅读 0
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值