Python学习笔记3

Python学习笔记–3.函数式编程

MrZigZag@Peking University

2015-07-12

1.函数式编程

抽象层次比较高,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。

Python对函数式编程提供部分支持。

2.高阶函数

将简单的函数作为参数传入高阶函数。

def add(x, y, f):
    return f(x) + f(y)

>>> add(-5, 6, abs)
11

map()接收两个参数,一个是函数另一个是序列,返回一个经函数处理过的序列。

def f(x):
    return x * x

>>> map(f, range(1,10))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce()是把一个函数f作用在一个序列list上,函数f接收两个参数,因而是逐个累积运算。

reduce(f, [1, 2, 3]) = f(f(1, 2), 3)

利用reduce可以轻松将一个string转换成int,例如

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    return reduce(fn, map(char2num, s))

filter()过滤函数,给定一个序列和一个函数,函数依次作用于序列并根据结果的True/False决定是否丢弃。

def is_odd(n):
    return n % 2 == 1

>>> filter(is_odd, range(0, 10))
[1, 3, 5, 7, 9]

例如删除1-100之间的素数。

def isn_prime(num):
    if num == 1:
        return True
    for i in range(2, int(math.sqrt(num)) + 1):
        if (num % i == 0):
            return True
    return False

filter(isn_prime, range(1, 101))

sorted()对一个list排序,默认对数字是升序排序,字符串则是首字母ASCII码。sorted()是一个高阶函数,可以输入一个比较函数实现自定义排序。

>>> sorted([13, 2, 90, 33, 14, 5, 1])
[1, 2, 5, 13, 14, 33, 90]

def reversed_cmp(x, y):
    if x > y:
        return -1
    elif x == y:
        return 0
    else:
        return 1
>>> sorted([13, 2, 90, 33, 14, 5, 1], reversed_cmp)
[90, 33, 14, 13,5, 2, 1]

3.返回函数和闭包

高阶函数除了接收函数作为参数,还可以把函数作为结果值返回。返回的函数包含了相关的参数。

def delay_sum(*args):
    def sum():
        ans = 0
        for n in args:
            ans = ans + n
        return ans
    return sum

>>> f = delay_sum(1, 2, 3, 4)
>>> f()
10

闭包内部函数可以引用外部函数的参数和局部变量,外部函数返回内部函数时,相关的参数和变量都保存在返回的函数中。每次调用外部函数都会返回一个新的函数,即使传入相同的参数。因此不同的返回函数相互之间没有影响。

返回函数不要引用任何后续会发生变化的变量,尤其是循环变量。返回函数只有在显式调用时才会使用变量执行。

def counter():
    fs = []
    for i in range(1, 4):
        def f():
            return i * i
        fs.append(f)
    return fs

>>> f1, f2, f3 = counter()
>>> f1()
9
>>> f2()
9
>>> f3()
9

一定要引用循环变量时可以新建一个函数,绑定循环变量。

def counter():
    fs = []
    for i in range(1, 4):
        def f(j):
            def g():
                return j * j
            return g
        fs.append(f(j))
    return fs

>>> f1, f2, f3 = counter()
>>> f1()
1
>>> f2()
4
>>> f3()
9

或者用匿名函数lambda来实现。

>>> f1, f2, f3 = [(lambda i = i:i * i) for i in range(1, 4)]

4.匿名函数lambda

简单的形式可以直接使用匿名函数lambda *args:option,匿名函数只能有一个表达式,但是没有名字,因此不用担心函数名冲突。

匿名函数可以赋值给一个变量,也可以作为返回值。

def test_lambda(x, y):
    return lambda: x * x + y * y

匿名函数作为参数传入到高级函数中,具有简单直观的效果。

>>> map(lambda x:x * x, range(1, 10))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

5.装饰器Decorator

对于函数对象,在代码运行期间动态增加功能而没有修改原函数的方式称为装饰器。本质上装饰器就是一个返回函数的高阶函数。

def log(func):
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

定义好了Decorator之后,可用@将它置于函数的定义处,这样执行该函数时就会执行装饰器。即相当于执行了func = log(func)

@log
def test():
    print 'Hello world!'

>>> test()
call test():
Hello world!

如果decorator本身需要传入参数,那就得写一个返回decorator的高阶函数。

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print '%s %s():' % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

6.偏函数

可以把一个函数的某些参数固定住,然后返回一个新的函数。

import functools
int2 = functool.partial(int, base = 2)

>>> int2('1001')
9

实际上就相当于

kw = {'base' : 2}
int('1001', kw)