# 可迭代对象&迭代器
> **可迭代对象**:使用内置的iter()函数可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法,那么对象就是是可迭代的。序列都可以迭代;实现了__getitem__方法,而且其参数是从零开始的索引,这样的对象也可以迭代。
>
> **迭代器**:实现了无参数的__next__方法,返回序列中的下一个元素,如果没有元素来了,那么抛出StopIteration异常。在Python中,迭代器还实现了__iter__方法(返回本身),因此迭代器也可以迭代。
## 序列都是可迭代的
为了“序列都可以迭代”,我们先看一个实现了序列协议的类:Sentence(通过索引从文本中提取单词):
```
import re
RE_WORD = re.compile('\w+')
class Sentence:
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)
s = Sentence('hello wolrd')
for word in s:
print(word)
------------------------------
hello
world
```
从这个例子中我们知道:Sentence实例(也就是一个**序列**)是**可以迭代**的。
## python内置的iter函数
Question思考一个问题,我们自定义的序列Sentence是没有实现__iter__方法返回迭代器,Python为什么也可以从中获取到迭代器呢?
为了理解上面的问题,我们需要了解Python内置的iter函数。
Python解析器需要迭代对象x时,会自动调用iter(x),内置的iter函数有以下作用:
(1)检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器;
(2)如果没有实现__iter__方法,但是实现了__getitem__方法,Python解释器会创建一个迭代器,尝试按顺序(从索引0开始),获取元素;(之所以这样处理是为了向后兼容);
```
s = Sentence('hello wolrd')
it = iter(s) # 通过iter()获取一个迭代器
print(it) # <iterator object at 0x7fc53807c5f8>
print(type(it)) # <class 'iterator'>
while True: # 模拟for in 循环
try:
word = next(it)
except StopIteration as e:
del it
break
print(word)
-----------------------------
<iterator object at 0x7fc53807c5f8>
<class 'iterator'>
hello
world
```
(3)如果尝试失败,Python解释器会抛出TypeError异常,通常会提示“object is not iterable”。
通过理解Python内置的iter函数,我们可以知道:
- **任何Python序列都可迭代(其实标准的序列也都实现了__iter__方法)**
- **Python会从可迭代的对象中获取迭代器**
## 典型的迭代器
> 可迭代对象一定不能是自身的迭代器。也就是说可迭代对象必须实现__iter__方法,但是不能实现__next__方法。
> 另一方面,迭代器的__iter__方法应该返回自身。
为了清楚的说明可迭代的对象和迭代器之间的重要区别,我们再看一个使用迭代模式实现的Sentence类:
```
import re
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(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 # 迭代器返回自身
```
Question1为什么迭代器需要实现__iter__方法?
- 以便于在应该使用可迭代对象的地方使用迭代器,例如for循环
- 迭代器可以通过issubclass(SentenceInterator, abc.Iterator)的测试
Question2为什么可迭代对象不能实现__next__方法,让自身即是可迭代对象,也是一个迭代器呢?
因为这样做是反设计模式的。、
在《设计模式:可复用面向对象软件的基础》一书讲解迭代器设计模式是,在“适用性”一节中说,迭代模式可以用来
- 访问一个聚合对象的内容而无需暴露它内部的接口;
- 支持对聚合对象的多种遍历;
- 为遍历不同的聚合结构提供一个统一的接口;
为了“支持多种遍历”,必须能从同一个可迭代对象中获取多个独立的迭代器,而且各个迭代器都要能维护自身的内部状态,也就是说每次调用iter(my_iterable)都新建一个独立的迭代器。
生成器
生成器函数
- 生成器函数:函数体内有yield关键字
- 函数返回值:生成器对象
- 函数执行顺序:在生成器对象每次调用next()时执行,遇到yield语句返回,再次调用执行时从上次返回的yield语句处继续执行
def generator_func():
print("step 1")
yield 1
print("step 2")
yield 2
print("step 3")
yield 3
def normal_func():
return 1
if __name__ == '__main__':
g = generator_func() # <generator object generator_func at 0x7ff0908cade0>
n = normal_func() # n: 1
# print(dis.dis(generator_func))
for i in g:
print("generator obj res: ", i)
"""
step 1
generator obj res: 1
step 2
generator obj res: 2
step 3
generator obj res: 3
"""
函数执行
从下面的例子中,让我们来了解函数的执行过程:
import inspect
frame = None
def foo():
bar()
def bar():
global frame
frame = inspect.currentframe() # 返回当前栈帧对象
foo()
print(frame.f_code.co_name) # 打印栈帧对象名 --> bar
caller_frame = frame.f_back # 获取调用栈帧对象
print(caller_frame.f_code.co_name) # 打印调用栈帧对象名 --> foo
python中函数调用的执行过程:
1)python.exe会通过C实现的函数:PyEval_EvalFrameEx()去执行定义的Python foo()函数
- 首先会创建一个栈帧对象(FrameObject)
- 栈帧对象会去执行foo()函数的Python字节码
2)当执行到调用bar()函数时
- 会再创建一个栈帧对象(FrameObject)
- 新的栈帧对象会去执行bar()函数的Python字节码
流程如图:
Python中函数的调用类似递归的过程,所有的堆栈对象都分配在堆内存上(不会随着函数执行完而销毁),这就决定了堆栈对象可以独立于调用者而存在。
生成器函数执行
CPython解释器在编译Python字节码时就会生产生成器对象,C函数PyEval_EvalFrameEx()会在堆栈对象的基础上进行封装为生成器对象(PyGenObject),生成器对象(PyGenObject),gi_frame指向一个新的PyFrameObject,其中保存了生成器对象上次执行的字节码位置(f_lasti),而gi_code则指向PyCodeObject用于保存生成器函数的字节码。
通过一个例子来看看其具体过程,首先通过dis来打印出生成器函数的Python字节码:
def generator_func():
yield 1
name = "zgt"
yield 2
age = 18
return "hello generator"
print(dis.dis(generator_func))
再通过next()函数来执行生成器对象,并打印其堆栈对象中内容:
g = generator_func()
print(g.gi_frame.f_lasti) # -1
print(g.gi_frame.f_locals) # {}
next(g)
print(g.gi_frame.f_lasti) # 2
print(g.gi_frame.f_locals) # {}
next(g)
print(g.gi_frame.f_lasti) # 12
print(g.gi_frame.f_locals) # {'name':'zgt'}
对比生成器函数的字节码,我们就可以理解Python的生成器的独特aazA处(延时求值)的实现原理。
标签:__,迭代,Python,self,生成器,iter,对象 From: https://www.cnblogs.com/zzggtt/p/17105420.html