可迭代对象、迭代器、生成器的理解
2017-10-04 23:31
651 查看
所有的生成器都是迭代器
关于迭代器和生成器的一种定义:迭代器用于从集合中取出元素;生成器用于凭空生成元素。
Python中,所有的集合都是可以迭代的,在Python语言内部,迭代器用于支持:
for 循环
构建和扩展集合类型
逐行遍历文本文件
列表推导,字典推导,集合推导
元组拆包
调用函数时,使用*拆包实参
如同标题本文的标题一样,这边文章主要讲解三个方面,可迭代对象,迭代器,生成器,下面逐个开始理解
上面代码的运行结果:
首先从结果来看,我们可以看出这个类的实例是可以迭代的,
并且我们从打印print(s)的结果可以看出,显示的也是我们定义的内容,如果我们在类中没有通过__repr__自定义,打印结果将为:
<__main__.Sentence object at 0x102a08fd0>
同时这里的实例化对象也是一个序列,所以我们可以通过s[0]这种方式来获取每个元素
我们都知道序列可以迭代,那么序列为啥可以迭代,继续深入理解
内置的iter函数作用:
检查对象是否实现了__iter__方法,如果实现调用它,获取一个迭代器
如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试按顺序(从0开始)获取元素
如果尝试失败,会抛出TypeError异常,通常会提示:“C object is not iterable”,其中C是目标对象所属的类
任何python序列可以迭代的原因是,他们都实现了__getitem__方法,并且标准的序列也实现了__iter__方法。
关于如何判断x对象是否为可迭代对象,有两种方法:iter(x)或者isinstance(x,abc.Iterable)
那么这两种判断法有什么区别么?
其实从Python3.4之后建议是通过iter(x)方法来进行判断,因为iter方法会考虑__getitem__方法,而abc.Iterable不会考虑,所以iter(x)的判断方法更加准确
就像我最开始写的那个例子,分别通过这两种方式来测试,可以看出,其实这个类是可以迭代的,但是通过abc.Iterable的方式来判断,确实不可迭代的
关于可迭代对象的一个小结:
使用iter内置函数可以获取迭代器的对象,如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的
序列都可以迭代
实现了__getitem__方法,而且其参数是从零开始的索引,这种对象也可以迭代
Python从可迭代的对象中获取迭代器
一个简单的例子,当我们循环字符串的时候,字符串就是一个可迭代的对象,背后就是有迭代器,只不过我们看不到,下面为代码例子:
这两种方式都可以获取可迭代对象里的内容,但是while循环的方式如果不通过try/except方式获取异常,最后就会提示StopIteration的错误,这是因为Python语言内部会处理for循环和其他迭代上下文(如列表推导,元组拆包等等)中的StopIteration
标准的迭代器接口有两个方法:
__next__:返回下一个可用的元素,如果没有元素了抛出StopIteration异常
__iter__:返回self,以便在应该使用迭代器的地方使用迭代器,例如for循环
因为迭代器只需要__next__和__iter__两个方法,所以除了调用next()方法,以及捕获StopIteration异常之外,没有办法检查是否还有遗留元素,并且没有办法还原迭代器,如果想要再次迭代,就需要调用iter(...)传入之前构建迭代器的可迭代对象
我们把刚开始写的sentence类通过迭代器的方式来实现,要说的是这种写法不符合python的习惯做法,这里是为了更好的理解迭代器和可迭代对象之间的重要区别
这样我们就可以很清楚的明白,我们定义了一个SenteneIterator是一个迭代器,也实现了迭代器应该有的两种方法:__next__和__iter__方法,这样我们通过 issubclass(SentenceIterator,abc.Iterator)检查
这里我们还能看到可迭代对象和迭代器的区别:
可迭代对象有__iter__方法,每次都实例化一个新的迭代器
迭代器要实现__next__和__iter__两个方法,__next__用于获取下一个元素,__iter__方法用于迭代器本身,因此迭代器可以迭代,但是可迭代对象不是迭代器
有人肯定在想在Sentence类中实现__next__方法,让Sentence类既是可迭代对象也是自身的迭代器,但是这种想法是不对的,这是也是常见的反模式。所以可迭代对象一定不能是自身的迭代器
在上面这个代码中,我们通过yield关键字,这里的__iter__函数其实就是生成器函数,迭代器其实是生成器对象,每次调用__iter__方法,都会自动创建。
生成器函数会创建一个生成器对象,包装生成器函数的定义体,把生成器传给next(...)函数时,生成器函数会向前,执行函数定义体中的下一个yield语句,返回产出的值,并在函数定义体的当前位置暂停,最终,函数的定义体返回时,外层的生成器对象会抛出SotpIteration异常,这一点和迭代器协议一致。
下面是一个生成器的例子:
这里其实我们要明白进行for循环的过程其实就是在隐式的调用next()函数
当我们写了好几种Sentence类的时候,感觉我们通过生成器方式实现的挺简单了,其实还有更简单的方法的,代码例子如下,这里的finditer函数构建了一个迭代器:
关于实现Sentence,还可以通过生成器表达式。
关于迭代器和生成器的一种定义:迭代器用于从集合中取出元素;生成器用于凭空生成元素。
Python中,所有的集合都是可以迭代的,在Python语言内部,迭代器用于支持:
for 循环
构建和扩展集合类型
逐行遍历文本文件
列表推导,字典推导,集合推导
元组拆包
调用函数时,使用*拆包实参
如同标题本文的标题一样,这边文章主要讲解三个方面,可迭代对象,迭代器,生成器,下面逐个开始理解
可迭代对象
先通过下面单词序列例子来理解:import re import reprlib RE_WORD = re.compile('\w+') class Sentence(object): def __init__(self,text): self.text = text self.words = RE_WORD.findall(text) def __getitem__(self, index): return self.words[index] def __len__(self): return len(self.words) def __repr__(self): """ 用于打印实例化对象时,显示自定义内容, reprlib.repr函数生成的字符换最多有30个字符,当超过怎会通过省略号显示 :return: 自定义内容格式 """ return 'Sentence(%s)' % reprlib.repr(self.text) s = Sentence('"the time has come," the Walrus said,') print(s) print(type(s)) for word in s: print(word) print(list(s))
上面代码的运行结果:
首先从结果来看,我们可以看出这个类的实例是可以迭代的,
并且我们从打印print(s)的结果可以看出,显示的也是我们定义的内容,如果我们在类中没有通过__repr__自定义,打印结果将为:
<__main__.Sentence object at 0x102a08fd0>
同时这里的实例化对象也是一个序列,所以我们可以通过s[0]这种方式来获取每个元素
我们都知道序列可以迭代,那么序列为啥可以迭代,继续深入理解
序列可以迭代原因
解释器需要迭代对象x时,会自动调用iter(x)内置的iter函数作用:
检查对象是否实现了__iter__方法,如果实现调用它,获取一个迭代器
如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试按顺序(从0开始)获取元素
如果尝试失败,会抛出TypeError异常,通常会提示:“C object is not iterable”,其中C是目标对象所属的类
任何python序列可以迭代的原因是,他们都实现了__getitem__方法,并且标准的序列也实现了__iter__方法。
关于如何判断x对象是否为可迭代对象,有两种方法:iter(x)或者isinstance(x,abc.Iterable)
那么这两种判断法有什么区别么?
其实从Python3.4之后建议是通过iter(x)方法来进行判断,因为iter方法会考虑__getitem__方法,而abc.Iterable不会考虑,所以iter(x)的判断方法更加准确
就像我最开始写的那个例子,分别通过这两种方式来测试,可以看出,其实这个类是可以迭代的,但是通过abc.Iterable的方式来判断,确实不可迭代的
关于可迭代对象的一个小结:
使用iter内置函数可以获取迭代器的对象,如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的
序列都可以迭代
实现了__getitem__方法,而且其参数是从零开始的索引,这种对象也可以迭代
迭代器
首先我们要明白可迭代的对象和迭代器之间的关系:Python从可迭代的对象中获取迭代器
一个简单的例子,当我们循环字符串的时候,字符串就是一个可迭代的对象,背后就是有迭代器,只不过我们看不到,下面为代码例子:
# 通过for循环方式 s = "ABC" for i in s: print(i) print(''.center(50, '-')) # 通过while循环方式 it = iter(s) while True: try: print(next(it)) except StopIteration: del it break
这两种方式都可以获取可迭代对象里的内容,但是while循环的方式如果不通过try/except方式获取异常,最后就会提示StopIteration的错误,这是因为Python语言内部会处理for循环和其他迭代上下文(如列表推导,元组拆包等等)中的StopIteration
标准的迭代器接口有两个方法:
__next__:返回下一个可用的元素,如果没有元素了抛出StopIteration异常
__iter__:返回self,以便在应该使用迭代器的地方使用迭代器,例如for循环
因为迭代器只需要__next__和__iter__两个方法,所以除了调用next()方法,以及捕获StopIteration异常之外,没有办法检查是否还有遗留元素,并且没有办法还原迭代器,如果想要再次迭代,就需要调用iter(...)传入之前构建迭代器的可迭代对象
我们把刚开始写的sentence类通过迭代器的方式来实现,要说的是这种写法不符合python的习惯做法,这里是为了更好的理解迭代器和可迭代对象之间的重要区别
import re import reprlib from collections import abc RE_WORD = re.compile('\w+') class Sentence: def __init__(self,text): self.text = text self.words = RE_WORD.findall(text) def __repr__(self): return "Sentence(%s)" % reprlib.repr(self.text) def __iter__(self): return SentenceIterator(self.words) class SentenceIterator: def __init__(self,words): self.words = words self.index = 0 def __next__(self): try: word = self.words[self.index] except IndexError: raise StopIteration() self.index += 1 return word def __iter__(self): return self
这样我们就可以很清楚的明白,我们定义了一个SenteneIterator是一个迭代器,也实现了迭代器应该有的两种方法:__next__和__iter__方法,这样我们通过 issubclass(SentenceIterator,abc.Iterator)检查
这里我们还能看到可迭代对象和迭代器的区别:
可迭代对象有__iter__方法,每次都实例化一个新的迭代器
迭代器要实现__next__和__iter__两个方法,__next__用于获取下一个元素,__iter__方法用于迭代器本身,因此迭代器可以迭代,但是可迭代对象不是迭代器
有人肯定在想在Sentence类中实现__next__方法,让Sentence类既是可迭代对象也是自身的迭代器,但是这种想法是不对的,这是也是常见的反模式。所以可迭代对象一定不能是自身的迭代器
生成器
先通过用生成器方式替换上个例子中SentenceIterator类,例子如下:import re import reprlib RE_WORD = re.compile('\w+') class Sentence: def __init__(self,text): self.text = text self.words = RE_WORD.findall(text) def __repr__(self): return 'Sentence(%s)' % reprlib.repr(self.text) def __iter__(self): for word in self.words: yield word
在上面这个代码中,我们通过yield关键字,这里的__iter__函数其实就是生成器函数,迭代器其实是生成器对象,每次调用__iter__方法,都会自动创建。
生成器的工作原理
Python函数定义体中有yield关键字,该函数就是生成器函数。生成器函数会创建一个生成器对象,包装生成器函数的定义体,把生成器传给next(...)函数时,生成器函数会向前,执行函数定义体中的下一个yield语句,返回产出的值,并在函数定义体的当前位置暂停,最终,函数的定义体返回时,外层的生成器对象会抛出SotpIteration异常,这一点和迭代器协议一致。
下面是一个生成器的例子:
这里其实我们要明白进行for循环的过程其实就是在隐式的调用next()函数
当我们写了好几种Sentence类的时候,感觉我们通过生成器方式实现的挺简单了,其实还有更简单的方法的,代码例子如下,这里的finditer函数构建了一个迭代器:
import re import reprlib RE_WORD = re.compile('\w+') class Sentence: def __init__(self,text): self.text = text def __repr__(self): return 'Sentence(%s)' % reprlib.repr(self.text) def __iter__(self): for match in RE_WORD.finditer(self.text): yield match.group()
关于生成器表达式
生成器表达式可以理解为列表推导的惰性版本,不会直接构成列表,而是返回一个生成器,按需惰性生成元素。关于实现Sentence,还可以通过生成器表达式。
import re import reprlib RE_WORD = re.compile('\w+') class Sentence: def __init__(self,text): self.text = text def __repr__(self): return 'Sentence(%s)' % reprlib.repr(self.text) def __iter__(self): return (match.group() for match in RE_WORD.finditer(self.text))
相关文章推荐
- 完全理解Python迭代对象、迭代器、生成器
- 完全理解 Python 迭代对象、迭代器、生成器
- 【python】14、完全理解可迭代对象、迭代器、生成器
- 完全理解PYTHON迭代对象、迭代器、生成器
- 完全理解 Python 迭代对象、迭代器、生成器
- 完全理解 Python 迭代对象、迭代器、生成器
- 完全理解 Python 迭代对象、迭代器、生成器
- 完全理解 Python 迭代对象、迭代器、生成器
- 完全理解Python迭代对象、迭代器、生成器
- 完全理解Python迭代对象、迭代器、生成器
- 完全理解Python迭代对象、迭代器、生成器
- 理解Python迭代对象、迭代器、生成器
- 完全理解 Python 迭代对象、迭代器、生成器
- Python之列表生成式、生成器、可迭代对象与迭代器
- python学习笔记--可迭代对象和迭代器的理解
- 第八章 Python可迭代对象、迭代器和生成器
- 迭代器、可迭代对象、生成器
- Python 可迭代的对象、迭代器和生成器
- 可迭代对象、迭代器与生成器
- Python3.可迭代对象_迭代器_生成器