Python 函数式编程之迭代器、生成器及其应用

python 标准库中提供了 itertools, functools, operator 三个库支持函数式编程,对高阶函数的支持,python 提供 decorator 语法糖。 迭代器 (iterator)和生成器(generator)概念是 python 函数式编程的基础,利用迭代器和生成器能够实现函数式编程中常常用到的 map(), filter(), reduce() 等过程以及 itertools, functools 中提供的绝大部分功能。html

一、迭代器和生成器基础(next, iter, yield)

迭代器和生成器依赖于 next(), iter() 方法和 yield 表达式java

1.1 next 函数

next(iterator[, default]) 是内置的函数,经过调用 __next__() 方法取得 iterator 的下一个元素,全部元素消耗完再调用就会引发 StopIteration 异常。若是提供了 default 参数,则当取完全部元素后,再调用 next 时会返回 default 值,而不是引发 StopIteration 异常。python

1.2 iter 函数

iter(object[, sentinel]) 内置函数会返回一个迭代器。没有第2个参数时, object 必须支持迭代协议(__iter__() 方法) 或序列协议 (__getitem__() 方法),不然会引发 TypeError 异常。若是有哨兵 (sentinel)参数, object 必须是可调用的对象,这种方式建立的迭代器每次调用 __next__() 方法时会以无参的形式调用 object ,若是返回值等于哨兵就会引发 StopIteration 异常,不然就返回这个值git

with open('mydata.txt') as fp:
    for line in iter(fp.readline, ''):
        process_line(line)

1.3 yield 表达式

yield 英文意思是生产,是 python 的关键字,在函数返回值时用来替换 return 产生一个值。yield 表达式只能用于定义生成器函数中,在函数外使用 yield 会致使 SyntaxError: 'yield' outside function 。express

生成器控制生成器函数的执行,当调用生成器的某个函数时,开始执行,遇到第一个 yield 表达式时,返回 yield 后面表达式的值,而后被挂起(suspend),挂起时保持全部的局部状态,包括局部变量绑定、指令指针、内部的求值栈、异常处理状态;当再次调用生成器的某个方法时,执行流会恢复。编程

因此生成器函数很是像协程(coroutine,其它语言中的概念),二者都会 yield 屡次,有多个入口,执行流会被挂起。惟一的区别是生成器函数不能控制 yield 以后,执行流应该从哪继续,控制老是被转移到生成器的调用者,因此又被称为半协程(semicoroutine)。app

二、迭代器(iterator)

迭代器(iterator)必须至少要定义 __iter__() 和 __next__() 两个方法,经过 iter() 和 next() 函数调用。 iter() 生成一个迭代器, next() 每调用一次都会返回下一个值,若是已经到最后一个值了,那么再调用 next() 就会引发 StopIteration 异常。编程语言

#python的迭代器类须要实现__iter__魔法方法返回迭代器实例,还须要实现一个next方法,在迭代到末尾时抛出StopIteration异常表示迭代结束。以下简单示例:
class SimpleIterator:
    def __init__(self, maxvalue):
        self.current = 0
        self.max = maxvalue

    def next(self):
        result = self.current
        self.current += 1
        if result == self.max:
            raise StopIteration()
        return result

    def __iter__(self):
        return self


li = list(SimpleIterator(5))
print li

################################

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

for 循环实际上就是先将 iter() 做用到容器对象上生成迭代器,而后每次调用 next() ,当引发 StopIteration 时就终止 for 循环。ide

for element in ['a', 'b', 'c']:
    print(element)

# 等价于:
it = iter(['a', 'b', 'c'])
try:
    while True:
        print(next(it))
except StopIteration:
    pass

 

三、生成器(generator)

生成器其实就是一种特殊的迭代器,它使一种更为高级、更为优雅的迭代器,使用生成器让咱们能够以一种更加简洁的语法来定义迭代器,它与普通函数相同,只是返回值时用 yield 而不是 return,局部变量和执行状态在调用之间会自动保存。
让咱们先明确如下两点:函数式编程

  • 任意生成器都是迭代器(反过来不成立)
  • 任意生成器,都是一个能够延迟建立值的工厂
def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

#################################
        
def yield_test(maxvalue):
    i = -1
    while i < maxvalue-1:
        i += 1
        yield i
 
for i in yield_test(10):
    print i

能用 generator(函数) 实现的均可以用 iterator(类)实现,不过生成器 会自动建立 __iter__() 和 __next__() 方法,终止时也会自动引发 StopIteration 异常,于是显得更紧凑。

利用生成器表达式(generator expression)能够不用专门定义一个 generator 函数,直接就地使用。生成器表达式与列表表达式(list comprehension)相似,只不过用的是圆括号而不是方括号,因为生成器只保存上次执行的状态,因此相比列表表达式,生成器表达式占用内存更少。

四、流——序列

生成器本质上至关于函数式编程语言中的流的概念,流表面上看是一个序列,但这个序列不是一次构造出来的,而是在须要时构建,函数式编程语言中流是经过惰性求值实现的,能够看到 python 是经过关键词 yield 实现的。

使用流的概念能够避免命令式程序设计中赋值带来的反作用,同时更加简洁优雅。用序列模拟时间变化,至关因而坐标变换,当咱们观察一个正在移动的粒子时,咱们说该粒子的位置(状态)正在变化,而从粒子的世界线的观点看,这里就根本不涉及任何变化。

TODO : 补充例子

python3 将 python2 中许多列表改为了迭代器,更加函数式了,例如 range(), zip() 在 python2 中返回列表,而 python3 中返回一个迭代器,因为迭代器只是在须要(next())时取元素而不是一次就构建整个列表,因此能够表示很是大的序列甚至无穷序列。

五、生成器——迭代器方法(generator-iterator method)

生成器——迭代器方法 能够用来控制生成器函数的执行流

  • __next__()

  • send(value) 恢复执行流,并将 value 发送到生成器函数,value 做为当前 yield 表达式的值

  • throw(type[, value[, traceback]]) 在生成器暂停的地方引发 type 类型的异常,并返回生成器函数产生的下一个值

  • close

>>> def echo(value=None):
...     print("Execution starts when 'next()' is called for the first time.")
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception as e:
...                 value = e
...     finally:
...         print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
1
>>> print(next(generator))
None
>>> print(generator.send(2))
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.

六、应用

有了上面迭代器和生成器,就能够实现各类函数式编程了,下面是函数式编程中经常使用的几个函数,更多例子能够查看 itertools 文档

6.1 map 函数

当 map(function, iterable,...) 接收 n 个 iterable 时,每次在各 iterable 中各取一个元素传给 function 做参数,因此 function 必须可以接收 n 个参数,当各个 iterable 长度不同时按最短的终止,例如 map(lambda x,y: x+y, [1,2], [3,4], [5,6]) 会报错, map(lambda x,y: x+y, 'abcd', 'def') 返回的迭代器依次为 'ad', 'be', 'cf'

# 这个实现很差,用到了 zip,不过 zip 也能够经过生成器实现(见后面)
def map(function, *iterables):
    for args in zip(*iterables):
        yield function(*args)

itertools.starmap(function, iterable) 只接收一个 iterable,当 function 接收多个参数时,各个参数是放在元组中的,例如 itertools.starmap(pow, [(2,5), (3,2), (10,3)]) 返回迭代器的值依次为 32, 9, 1000。

def starmap(function, iterable):
    for args in iterable:
        yield function(*args)

6.2 filter 函数

filter(function, iterable) 函数至关于生成器表达式 (item for item in iterable if function(item)) ,没有提供 function 参数时至关于 (item for item in iterable if item)

itertools 中提供 filterfalse(predicate, iterable) 函数, filterfalse(lambda x: x%2, range(10)) 获得 0,2,4,6,8, 的迭代器

def filterfalse(predicate, iterable):
    if predicate i None:
        predicate = bool
    for x in iterable:
        if not predicate(x):
            yield x

6.3 reduce 函数

reduce(function, iterable[, initializer]) 函数将 function 从左到右两个两个地累计做用到 iterable 上,从而将 iterable 归约到一个值,例如 reduce(lambda x, y: x+y, [1,2,3,4]) 会计算 (((1+2)+3)+4),从而获得10。 python3 已经将内置的 reduce 函数移到 functools 模块中了

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value

6.4 枚举函数(enumerate)

enumerate(iterable, start=0) 生成一个枚举迭代器,每次调用 next() 时会返回一个元组,包含计数(从 start 开始)和值(iterable)

seasons = ['Spring', 'Summer', 'Fall', 'Winter']
list(enumerate(seasons)) # => [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
list(enumerate(seasons, start=1)) # => [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

能够利用生成器实现 enumerate

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

6.5 zip 函数

zip(*iterables) 返回元组迭代器,iterables 长度不一样时,按最短的截断, itertools 模块中有 zip_longest() 函数。

a = [1, 2, 3]
b = [1, 4, 9]
c = [1, 8, 27]
list(zip(a, b, c))
# => [(1,1,1), (2,4,8), (3,9,27)]

利用生成器实现 zip

def zip(*iterables):
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem in sentinel:
                return
            result.append(elem)
        yield tuple(result)

6.6 累积器(accumulate)

标准库 itertools 提供 accumulate(iterable[,func]) 函数,将 func 函数做用到 iterable 相邻元素上,累计起来,返回的也是一个迭代器。例如 accumulate([1,2,3,4,5]) 返回迭代器,其值依次为 1, 3, 6, 10, 15,而 accumulate([1, 2, 3, 4, 5], operator.mul) 则返回迭代器的值依次为 1, 2, 6, 24, 120

一样也能够用生成器实现 accumulate

def accumulate(iterable, func=operator.add):
    it = iter(iterable)
    total = next(it)
    yield total
    for element in it:
        total = func(total, element)
        yield total

6.7 循环函数(cycle)

itertools.cycle(iterable) 将 iterable 串起来做为 iterator 返回,是无穷循环。例如 cycle('ABCD') 返回迭代器,其值是 A B C D A B C D A …

利用生成器实现 cycle

def cycle(iterable):
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
            yield element

6.8 groupby 函数

利用迭代器实现 groupby

class groupby:
    # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
    def __init__(self, iterable, key=None):
        if key is None:
            key = lambda x: x
        self.keyfunc = key
        self.it = iter(iterable)
        self.tgtkey = self.currkey = self.currvalue = object()
    def __iter__(self):
        return self
    def __next__(self):
        while self.currkey == self.tgtkey:
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)
        self.tgtkey = self.currkey
        return (self.currkey, self._grouper(self.tgtkey))
    def _grouper(self, tgtkey):
        while self.currkey == tgtkey:
            yield self.currvalue
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)

 

七、Refer:

[1] Python 函数式编程

http://dengshuan.me/techs/python-functional.html

[2] Java FP: Java中函数式编程的谓词函数(Predicates)第一部分

http://ifeve.com/functional-style-in-java-with/

[3] itertools — 建立高效迭代器的函数

http://python.usyiyi.cn/python_278/library/itertools.html

[4] itertools — Functions creating iterators for efficient looping (高效循环迭代器建立函数)

http://data.digitser.net/python_3.4.2/zh-CN/library/itertools.html

[5] PYTHON-进阶-ITERTOOLS模块小结

http://wklken.me/posts/2013/08/20/python-extra-itertools.html

[6] (译)Python关键字yield的解释(stackoverflow)

http://pyzh.readthedocs.org/en/latest/the-python-yield-keyword-explained.html

[7] Python yield 使用浅析

http://my.oschina.net/leejun2005/blog/94175

[8] Python函数式编程指南(三):迭代器

http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html

[9] Python函数式编程指南(四):生成器

http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html

[10] 可迭代对象 vs 迭代器 vs 生成器

http://python.jobbole.com/86258/

相关文章
相关标签/搜索