Python通过for循环理解迭代器和生成器实例详解


Posted in Python onFebruary 16, 2019

本文实例讲述了Python通过for循环理解迭代器和生成器。分享给大家供大家参考,具体如下:

迭代器

可迭代对象

通过 for…in… 循环依次拿到数据进行使用的过程称为遍历,也叫迭代。我们把可以通过 for…in… 语句迭代读取数据的对象称之为可迭代对象。

- 通过 isinstance()可以判断一个对象是否可以迭代

# 判断列表
print(isinstance([], Iterable)

打印结果为 True 即为可迭代对象。

- 自定义一个能容纳数据的类,测试该类的可迭代性

import collections
class MyClassmate(object):
  def __init__(self):
    self.names = []
  def add(self, name):
    self.names.append(name)
# 创建 MyClassmate对象
my_classmate = MyClassmate()
my_classmate.add("小王")
my_classmate.add("小李")
my_classmate.add("小张")
# 判断MyClassmate是否为可迭代对象
print("是否为可迭代对象:",isinstance(my_classmate, collections.Iterable))
# 迭代数据
for temp in my_classmate:
  print(temp)

运行结果:

是否为可迭代对象: False
Traceback (most recent call last):
    for temp in my_classmate:
TypeError: 'MyClassmate' object is not iterable

封装一个可以存放多条数据的类型是不可迭代的

何为可迭代对象

  • 我们分析对可迭代对象进行迭代使用的过程,发现每迭代一次(即在 for…in… 中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一个"人"去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的"人"称为迭代器 (Iterator)。
  • 可迭代对象的本质就是提供一个这样的中间"人"即迭代器帮助我们对其进行迭代遍历使用。
  • 可迭代对象通过__iter__方法向我们提供一个迭代器,在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.

1.可迭代对象的本质就是提供一个这样的中间"人"即迭代器帮助我们对其进行迭代遍历使用

2.可迭代对象是一个具备了__iter__方法的对象,通过__iter__方法获取可迭代对象的迭代器

from collections import Iterable
class MyClassmate(object):
  def __init__(self):
    self.names = []
  def add(self, name):
    self.names.append(item)
  def __iter__(self):
    """空实现该方法"""
    return None
# 创建 MyClassmate对象
my_classmate = MyClassmate()
my_classmate.add("小王")
my_classmate.add("小李")
my_classmate.add("小张")
# 判断MyClassmate是否为可迭代对象
print(isinstance(my_classmate, Iterable))

运行结果:

是否为可迭代对象: True

这回测试发现添加了__iter__方法的my_classmate对象已经是一个可迭代对象了。

iter() 函数与 next() 函数

list、tuple 等都是可迭代对象,我们可以通过 iter() 函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用 next() 函数来获取下一条数据。

li = [11, 22, 33, 44, 55]
# 通过iter() 取得可迭代对象的迭代器
iterator = iter(li)
# 通过next()函数取得iterator迭代器指向的下一个值
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))

1.iter(iterable) 函数是把可迭代对象的迭代器取出来,内部是调用可迭代对象的__iter__方法,来取得迭代器的

2.next(iterator) 函数是通过迭代器取得下一个位置的值,内部是调用迭代器对象的__next__方法,来取得下一个位置的值

注意: 当我们已经迭代完最后一个数据之后,再次调用 next() 函数会抛出 StopIteration 的异常,来告诉我们所有数据都已迭代完成,不用再执行 next() 函数了。

迭代器

我们要想构造一个迭代器,就要实现它的__next__ 方法。但这还不够,python 要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__ 方法,而 __iter__ 方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的 __iter__ 方法返回自身即可。

一个实现了 __iter__ 方法和 __next__ 方法的对象,就是迭代器,迭代器同时也是一个可迭代对象.

import collections
class MyClassmate(object):
  def __init__(self):
    # 声明一个列表
    self.names = []
    # 记录迭代器迭代的位置, 默认是0 ,即从起始位置开始
    self.current = 0
  def add(self, name):
    self.names.append(name)
  def __iter__(self):
    """通过该方法取得迭代器对象"""
    return self
  def __next__(self):
    """取得下一个迭代的值"""
    if self.current < len(self.names):
      name = self.names[self.current]
      self.current += 1
      return name
    else:
      raise StopIteration
# 创建MyClassmate实例
my_classmate = MyClassmate()
my_classmate.add("小王")
my_classmate.add("小李")
my_classmate.add("小张")
# 测试MyList是不是可迭代对象
print(isinstance(my_classmate, collections.Iterable))
# 遍历数据
for name in my_classmate:
  print(name)

for…in… 循环的本质

for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象 Iterable 的迭代器,然后对获取到的迭代器不断调用 next() 方法来获取下一个值并将其赋值给 item,当遇到 StopIteration 的异常后循环结束 (for…in..会自动处理 StopIteration 异常)。

生成器

生成器

生成器是一种特殊的迭代器,它比迭代器更优雅

创建生成器的方法

1.将列表生成式的 [] 改成 ()

# 参考列表生成式
L=[x*2 for x in range(6)]
print(L)
# 把[] 改为() :就是一个简单的列表生成器
G=(x*2 for x in range(6))
# 输出的是生成器对象
print(G)
print("通过next()函数取得下一个值")
print(next(G))
print(next(G))
print(next(G))
print(next(G))
print(next(G))
print(next(G))
# 创建一个简单生成器,通过 for来遍历
G=(x*2 for x in range(6))
print("通过for 迭代的结果:")
for num in G:
  print(num)

运行结果:

[0, 2, 4, 6, 8, 10]
<generator object <genexpr> at 0x7ff7f8bbd5c8>
通过next()函数取得下一个值
0
2
4
6
8
10
通过for 迭代的结果:
0
2
4
6
8
10

2.通过关键字 yield 实现生成器

def fib(n):
  current_index = 0
  num1, num2 = 0, 1
  while current_index < n:
    # print(num1) # 打印斐波那契数列
    """
     1. 假如函数中有yield,则不再是函数,而是生成器
     2. yield 会产生一个断点
     3. 假如yield后面紧接着一个数据,就会把数据返回,
      作为next()函数或者for ...in...迭代出的下一个值
    """
    yield num1
    num1, num2 = num2, num1 + num2
    current_index += 1
if __name__ == '__main__':
  # 假如函数中有yield,则不再是函数,而是一个生成器
  gen = fib(5)
  #  生成器是一种特殊的迭代器
  for num in gen:
    print(num)
  # 也可以用 next() 函数取下一个值

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将打印输出方式换成 yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在函数中有 yield 关键字,就称为生成器。

此时按照调用函数的方式( 案例中为 gen = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为 gen ),然后就可以按照使用迭代器的方式来使用生成器了。

使用 send() 唤醒

def gen():
  i = 0
  while i < 5:
    temp = yield i
    print(temp)
    i += 1
if __name__ == '__main__':
  # 取得生成器对象
  obj = gen()
  # 使用next()唤醒生成器
  print(next(obj))
  print(next(obj))
  # 使用send唤醒生成器 ,在唤醒的同时向断点处传入一个附加数据
  print(obj.send("haha"))
  # 使用next()唤醒生成器
  print(next(obj))
  # 使用send唤醒生成器 ,在唤醒的同时向断点处传入一个附加数据
  print(obj.send("python"))

运行结果:

0
None
1
haha
2
None
3
python

我们除了可以使用 next() 函数来唤醒生成器继续执行外,还可以使用 send() 函数来唤醒执行。使用 send() 函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

总结

1. 假如函数中有 yield,则不再是函数,而是生成器
2. yield 会产生一个断点,暂停函数,保存状态
3. 假如yield后面紧接着一个数据,就会把数据返回,作为 next() 函数或者 for …in… 迭代出的下一个值
4. 可以通过 next() 唤醒生成器,让生成器从断点处继续执行

send与next唤醒生成器不同:

1. send 与next都可以唤醒生成器,但send(value)可以传值给生成器的断点处
2. 使用:

next(generator)
generator.send("你好")

3. generator.send(None)等价于next(generator)
4. 注意: 第一次唤醒生成器时,假如使用 send,则只能传 None,因为刚开始执行生成器时,是没有断点的

- 解析

temp = yield num
generator.send("你好")

temp = yield num 为赋值语句,当看到等号时, 一定是等号左边先运行完,再赋值给等号右边

而程序运行到 yield num 时,会先返回一个值,也就是此时的 num ,然后将 send()里的参数传给 yield num,进而赋值给 temp

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
从零学Python之入门(三)序列
May 25 Python
举例讲解Python中is和id的用法
Apr 03 Python
Python中利用sorted()函数排序的简单教程
Apr 27 Python
python使用os.listdir和os.walk获得文件的路径的方法
Dec 16 Python
python爬虫爬取网页表格数据
Mar 07 Python
Jacobi迭代算法的Python实现详解
Jun 29 Python
Python3enumrate和range对比及示例详解
Jul 13 Python
opencv中图像叠加/图像融合/按位操作的实现
Apr 01 Python
浅谈Python中os模块及shutil模块的常规操作
Apr 03 Python
pycharm实现print输出保存到txt文件
Jun 01 Python
pandas分批读取大数据集教程
Jun 06 Python
Python下opencv使用hough变换检测直线与圆
Jun 18 Python
Python3 导入上级目录中的模块实例
Feb 16 #Python
对Python3 goto 语句的使用方法详解
Feb 16 #Python
解决Pycharm调用Turtle时 窗口一闪而过的问题
Feb 16 #Python
Python实现定时自动关闭的tkinter窗口方法
Feb 16 #Python
对IPython交互模式下的退出方法详解
Feb 16 #Python
python交互界面的退出方法
Feb 16 #Python
详解重置Django migration的常见方式
Feb 15 #Python
You might like
php一些错误处理的方法与技巧总结
2013/08/10 PHP
ThinkPHP框架下微信支付功能总结踩坑笔记
2019/04/10 PHP
用JavaScrpt实现文件夹简单轻松加密的实现方法图文
2008/09/08 Javascript
Firefox outerHTML实现代码
2009/06/04 Javascript
Javascript document.referrer判断访客来源网址
2020/05/15 Javascript
浅谈JavaScript事件的属性列表
2015/03/01 Javascript
JavaScript中定义函数的三种方法
2015/03/12 Javascript
JavaScript定义全局对象的方法示例
2017/01/12 Javascript
4个顶级开源JavaScript图表库
2018/09/29 Javascript
浅谈layui使用模板引擎动态渲染元素要注意的问题
2019/09/14 Javascript
JavaScript隐式类型转换代码实例
2020/05/29 Javascript
Python实现CET查分的方法
2015/03/10 Python
Python3.2中的字符串函数学习总结
2015/04/23 Python
Django中处理出错页面的方法
2015/07/15 Python
举例讲解Python设计模式编程中对抽象工厂模式的运用
2016/03/02 Python
总结python实现父类调用两种方法的不同
2017/01/15 Python
python实现推箱子游戏
2020/03/25 Python
python实现二维插值的三维显示
2018/12/17 Python
python最小生成树kruskal与prim算法详解
2019/01/17 Python
Pandas透视表(pivot_table)详解
2019/07/22 Python
python生成requirements.txt的两种方法
2019/09/18 Python
Python实现图片批量加入水印代码实例
2019/11/30 Python
Python读取YAML文件过程详解
2019/12/30 Python
python爬虫beautifulsoup库使用操作教程全解(python爬虫基础入门)
2021/02/19 Python
CSS3中的元素过渡属性transition示例详解
2016/11/30 HTML / CSS
2分钟教你实现环形/扇形菜单(基础版)
2020/01/15 HTML / CSS
this关键字的作用
2016/01/30 面试题
人事主管岗位职责范本
2013/12/04 职场文书
《匆匆》教学反思
2014/02/22 职场文书
六一儿童节主持词
2014/03/21 职场文书
2014年惩防体系建设工作总结
2014/12/01 职场文书
步步惊心观后感
2015/06/12 职场文书
《月光曲》教学反思
2016/02/16 职场文书
初中政治教师教学反思
2016/02/23 职场文书
Redis遍历所有key的两个命令(KEYS 和 SCAN)
2021/04/12 Redis
Django路由层如何获取正确的url
2021/07/15 Python