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

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

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

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

1.1 next 函数

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

1.2 iter 函数

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

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 。

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

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

2、迭代器(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 循环。

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

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

3、生成器(generator)

生成器(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)类似,只不过用的是圆括号而不是方括号,由于生成器只保存上次执行的状态,所以相比列表表达式,生成器表达式占用内存更少。

4、流——序列

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

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

TODO : 补充例子

python3 将 python2 中许多列表改成了迭代器,更加函数式了,例如 range(), zip() 在 python2 中返回列表,而 python3 中返回一个迭代器,由于迭代器只是在需要(next())时取元素而不是一次就构建整个列表,所以可以表示非常大的序列甚至无穷序列。

5、生成器——迭代器方法(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.

6、应用

有了上面迭代器和生成器,就可以实现各种函数式编程了,下面是函数式编程中常用的几个函数,更多例子可以查看 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)

7、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

时间: 2024-10-11 13:38:42

Python 函数式编程之迭代器、生成器及其应用的相关文章

python高级编程之迭代器与生成器

# -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' #迭代器与生成器 #--------------------------------------- #迭代器基于2个方法 """ next:返回容器下一个项目 __iter__:返回迭代器本身 """ #通过内建函数和序列来创建 i=iter('abc') print i.next()#a print i.next(

深入浅出 Python 函数式编程

1.函数式编程的定义与由来 如果程序中的函数仅接受输入并产生输出,即输出只依赖于输入,数据不可变,避免保存程序状态,那么就称为函数式编程(Functional Programming,简称FP,又称泛函编程). 这种风格也称声明式编程(Declarative Programming),与之相对的是指令式编程(Imperative Programming),后者中的对象会不断修改自身状态.函数式编程强调程序的执行结果比执行过程更重要,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算

【人生苦短 Python当歌】——Python函数式编程01

对Python有一定了解的人应该知道,Python并不是一门函数式编程语言,而是一门支持多种范式的语言,这也使得在Python中也能实现函数式编程, 对于学习到Python函数式编程的朋友,在这里推荐大家看一本名字叫<Python函数式编程>(Functional Programming in Python)从这本书中你应该能收获不少: 怎么说呢,函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就把复杂的任务分解成简单的任务,这种就称为面向过程的程序设

python函数式编程,列表生成式

1.python 中常见的集中存储数据的结构: 列表 集合 字典 元组 字符串 双队列 堆 其中最常见的就是列表,字典. 2.下面讲一些运用循环获取字典列表的元素 1 >>> dic={'name':'zhangsan','age':24,'city':'jinhua'} 2 >>> for key,value in dic.items(): 3 print(key,value) 4 5 6 name zhangsan 7 age 24 8 city jinhua 循环

我是如何开始去了解Python函数式编程--Python函数式编程初涉

Python函数式编程 1. 开始我们会了解什么是函数式编程: 函数:function 函数式:functional,一种编程范式 函数式编程特点:把计算视为函数而非指令,贴近计算 纯函数式编程:不需要变量,没有副作用,测试简单,支持高阶函数,代码简洁 Python支持的函数式编程特点: 不是纯函数式编程:允许有变量 支持高阶函数:函数也可以作为变量传入 支持闭包:有了闭包就能返回函数 有限度的支持匿名函数 2. 高阶函数 变量可以指向函数,函数名其实就是指向函数的变量,而高阶函数其实就是可以接

Python 函数式编程学习

描述:通过将函数作为参数,使得功能类似的函数实现可以整合到同一个函数. Before 1 def getAdd(lst): 2 result = 0 3 for item in lst: 4 result += item 5 return result 6 7 def getMul(lst): 8 result = 1 9 for item in lst: 10 result *= item 11 return result 12 13 print getAdd([1,2,3,4]) 14 pr

(转)Python函数式编程——map()、reduce()

转自:http://www.jianshu.com/p/7fe3408e6048 1.map(func,seq1[,seq2...]) Python 函数式编程中的map()函数是将func作用于seq中的每一个元素,并用一个列表给出返回值.如果func为None,作用通zip().当seq只有一个时,将func函数作用于这个seq的每一个元素上,得到一个新的seq. 举个例子来说明,(假设我们想要得到一个列表中数字%3的余数,那么可以写成下面的代码): >>> print map(la

Python函数式编程——map()、reduce()

文章来源:http://www.pythoner.com/46.html 提起map和reduce想必大家并不陌生,Google公司2003年提出了一个名为MapReduce的编程模型[1],用于处理大规模海量数据,并在之后广泛的应用于Google的各项应用中,2006年Apache的Hadoop项目[2]正式将MapReduce纳入到项目中. 好吧,闲话少说,今天要介绍的是Python函数式编程中的另外两个内建函数map()和reduce(),而不是Google的MapReduce. 1.ma

Python装饰器、迭代器&amp;生成器、re正则表达式、字符串格式化

Python装饰器.迭代器&生成器.re正则表达式.字符串格式化 本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 先定义一个基本的装饰器: ########## 基本装饰器 ########