您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Python

Python | 深入理解循环和迭代

时间:2023-02-06 15:28:38  来源:今日头条  作者:VT聊球

前言

循环,特别是for循环,是Python/ target=_blank class=infotextkey>Python中常见的语句,甚至于Guido van Rossum(Python创始人)在评论递归的时候说过在Python中“递归已死”,我想这句话的意思不是说在Python中不能用递归,而是说因为Python中的for循环语句足够强大,可以不考虑递归,而是用for循环实现原本用递归做的事情。

本文就在以上两本书所述基础上,从更深入和综合的角度进行阐述,以便能更好地使用for循环。

在实用for循环中,特别是初学者,会遇到很多坑,这里列举几个,看看你是否遇到过?

1、第二次无果

假设有一个数字组成的列表和一个生成器,生成器给出这些数字的平方:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)

用tuple函数,将squares转化为元组。

>>> tuple(squares)(1, 4, 9, 25, 49)

现在,又向计算这个生成器对象squares里面所有数字的和,观察一下,应该能看出来,其和是88,然而:

>>> sum(squares)0

这里计算结果为0,是Python的BUG吗?

2、检查无效

再用下面的方法得到那个生成器对象:

>>> numbers = [1, 2, 3, 5, 7]>>> squares = (n**2 for n in numbers)

如果检查9是否在squares生成器中,显然这是真的True。但是同样的检查如果再做一遍,就不是这个结果了——不可重复,不科学?

>>> 9 in squaresTrue>>> 9 in squaresFalse3、解包

创建一个包含两个键值对的字典:


>>> counts = {'Apples': 2, 'oranges': 1}

用多变量的赋值语句对字典解包:

>>> x, y = counts

先猜一下,这样做会有什么结果?报错,还是两个变量分别引用了两个键值对——引用键值对,兼职不可能吧,除非将键值对包裹在字典里。

但是,一没有报错,二没有返回键值对,而是:

'apples'

这似乎也合乎情理和逻辑。

传统的for循环

温故而知新,先来回顾一下for循环。

严格地说,Python中的for循环并不“传统”,或者说不符合众多语言中所继承的C语言风格的for循环。

先看一看所谓的C语言风格的for循环,以JAVAScript为例:

var numbers = [1, 2, 3, 5, 7];for (var i = 0; i < numbers.length; i += 1) {print(numbers[i]) }

像人们熟知的JavaScript, C, C++, Java, php等很多编程语言的for循环,都是这个样子的,所以,不少人认为这样的才是真正的for循环。

但是,Python盲从,而是特立独行地创造了自己的for循环。它不是C语言风格的,而是Python风格的:

numbers = [1, 2, 3, 5, 7]for n in numbers:print(n)

与传统的C语言风格的for循环不同,Python的for循环不需要创建索引,不需要对索引变量进行初始化,不需要进行边界检查,也不需要让索引递增。Python的for循环为我们完成了在numbers列表上循环的所有工作。

因此,Python中虽有for循环,但并非传统的C风格,那么其工作原理亦与之不同。

可迭代对象和序列

在Python中,可迭代对象就是可以用for来循环的东西。

for item in some_iterable:print(item)

序列是一种非常常见的可迭代对象,例如列表、元组和字符串都是序列。

>>> numbers = [1, 2, 3, 5, 7]>>> coordinates = (4, 5, 7)>>> words = "hello there"

序列是具有一组特定特征的可迭代对象,它们可以从0开始索引,并在比序列长度少一个元素的地方结束。它们有长度,并且可以切片。列表、元组、字符串和所有其他序列都是这样工作的。

>>> numbers[0]1>>> coordinates[2]7>>> words[4]'o'

Python中的很多东西都是可迭代对象,但并非所有的可迭代对象都是序列。集合、字典、文件和生成器都是可迭代对象,但它们都不是序列。

>>> my_set = {1, 2, 3}>>> my_dict = {'k1': 'v1', 'k2': 'v2'}>>> my_File = open('some_file.txt')>>> squares = (n**2 for n in my_set)

因此,任何可以用for来循环的东西都是一个可迭代对象,例如序列,但是并非所有可迭代对象都是序列。

Python的for循环

前面已经显示了,Python的for循环不使用索引——这是不同于C语言分割的for循环之处。

不过,你可能会悄悄滴认为,如果非要用,Python的for循环肯定也能实现C语言风格,因为我们一向认为“C语言是任何东西的基础”。为此,我们使用while 循环和索引手动遍历一个可迭代对象:

numbers = [1, 2, 3, 5, 7]i = 0while i < len(numbers):print(numbers[i])i += 1

很显然,上面的循环方式只适合于序列类对象,对其它的并非完全使用,比如字典、集合。

比如使用索引手动遍历一个集合,我们将看到报错:

>>> fruits = {'lemon', 'apple', 'orange', 'watermelon'}>>> i = 0>>> while i < len(fruits):... print(fruits[i])... i += 1... Traceback (most recent call last): File "", line 2, inTypeError: 'set' object does not support indexing

集合不是序列,因此它们不支持索引。

在Python中,我们不能通过使用索引手动遍历每个可迭代对象。这对于不是序列的可迭代对象根本不起作用。

迭代器

在Python中,迭代器可以用于for循环。

什么是迭代器?它是驱动可迭代对象的一类对象。我们可以从任意可迭代对象那里生成迭代器。

这里有三个可迭代对象:集合、元组和字符串。

>>> numbers = {1, 2, 3, 5, 7}>>> coordinates = (4, 5, 7)>>> words = "hello there"

可以用Python内置的iter函数用上面的可迭代对象生成迭代器。

>>> iter(numbers)iterator object at 0x7f2b9271c860>>>> iter(coordinates)>>> iter(words)

有了迭代器,就把它传给内置函数next,从而获得它的下一项。

>>> numbers = [1, 2, 3]>>> my_iterator = iter(numbers)>>> next(my_iterator)1>>> next(my_iterator)2

每从迭代器中取出一项,那一项就从迭代器中“消失”了。如果到了迭代器的最后一项,还执行next,而实际上后面已经没有其他项了,这时候就会报出StopIteration异常。

>>> next(iterator)3>>> next(iterator)Traceback (most recent call last): File "", line 1,in StopIteration不用for的循环

在了解了迭代器、以及iter和next函数后,我们将尝试手动遍历一个可迭代对象,而不使用for循环。

不用for,就得用while了,Python中只有这么两个循环语句。

def funky_for_loop(iterable, action_to_do):for item in iterable:action_to_do(item)

为了去掉for,需要:

 

  1. 根据给定的可迭代对象生成迭代器
  2. 从迭代器中重复获取下一项
  3. 如果成功获得了下一项,则相当于执行for循环了
  4. 如果在获取下一项时遇到“StopIteration”异常,则停止循环
def funky_for_loop(iterable, action_to_do):iterator = iter(iterable)done_looping = Falsewhile not done_looping:try:item = next(iterator)except StopIteration:done_looping = Trueelse:action_to_do(item)

 

这里,其实是用while循环和迭代器重新发明了for循环。

上面的代码基本上定义了Python中循环的工作方式。如果你了解内置的iter和next函数在遍历对象时的工作方式,那么你就了解了Python的for循环是如何工作的,它们的工作过程是类似的。

实际上,通过上面的代码,不仅仅展示了for循环的工作原理,所有可迭代对象的循环都如此。

总结一下,迭代器协议是描述“Python中可迭代对象的循环如何工作的”的一种基本方式,它本质上是Python中iter和next函数所定义的,Python中所有形式的迭代都由迭代器协议提供支持。

迭代器协议也被用于for:

for n in numbers:print(n)

多重赋值也使用迭代器协议:

x, y, z = coordinates

下面这种使用*的表达式也使用迭代器协议:

a, b, *rest = numbers print(*numbers)

许多内置函数依赖于迭代器协议:

unique_numbers = set(numbers)

Python中任何与可迭代对象一起工作的东西都可能以某种方式使用迭代器协议。在Python中,每当你遍历一个可迭代对象时,都依赖于迭代器协议。

生成器是迭代器

迭代器看起来很酷,不过,它是不是用途有限呢?或者说作为普通的Python编程者,是不是不需要关心它呢?

非也。

迭代器很常见。

>>> numbers = [1, 2, 3]>>> squares = (n**2 for n in numbers)

此处得到的squares是一个生成器,生成器也是迭代器,这意味着你可以对生成器调用next,以获取其下一项:

>>> next(squares)1>>> next(squares)4

用for循环同样可以遍历生成器:

>>> squares = (n**2 for n in numbers)>>> for n in squares:... print(n)... 1 4 9

下面这句话,貌似废话,但是重要:迭代器是可迭代对象。

这就意味着,可以将迭代器对象作为iter函数的参数,生成一个新的迭代器对象。不是吗?

>>> numbers = [1, 2, 3]>>> iterator1 = iter(numbers)>>> iterator2 = iter(iterator1) # 迭代器对象作为参数

以上最终得到的iterator2是一个迭代器。不过,要注意,iterator1和iterator2的关系:

>>> iterator1 is iterator2True

iter函数的参数如果是一个迭代器,返回对象仍然是该迭代器对象自身。

结论:迭代器是可迭代对象,所有迭代器都是自己的迭代器。

def is_iterator(iterable):return iter(iterable) is iterable

困惑了吗?

继续。

迭代器没有长度,因此无法索引。这个认识必须要建立起来。

>>> numbers = [1, 2, 3, 5, 7]>>> iterator = iter(numbers)>>> len(iterator)TypeError: object of type 'list_iterator' has no len()>>> iterator[0]TypeError: 'list_iterator' object is not subscriptable

从Python程序员的角度来看,使用迭代器可以做的唯一有用的事情就是:将迭代器传给内置的next函数、或遍历迭代器:

>>> next(iterator)1>>> list(iterator)[2, 3, 5, 7]

如果我们第二次遍历迭代器,我们将一无所获:

>>> list(iterator)

这就是说,迭代器可以认为是一次性的惰性的可迭代对象,这意味着它们只能遍历一次。


 

正如上表中所示,可迭代对象并不总是迭代器,但迭代器总是可迭代对象:

所谓迭代器协议,即:

 

  1. 可以作为next函数的参数,从而获得对象的下一项,或者在没有其他项时引发StopIteration异常。
  2. 可以作为iter函数的参数,并返回自身。

 

反过来说,也成立:

 

  1. 任何可以传给iter而没有引发TypeError的对象都是可迭代对象。
  2. 任何可以传给next而没有引发TypeError的对象都是迭代器。
  3. 任何在传给iter时返回自身的对象都是迭代器。

 

这是Python中的迭代器协议。

迭代器无处不在

Python中的迭代器很多,例如:

>>> letters = ['a', 'b', 'c']>>> e = enumerate(letters)>>> e>>> next(e)(0, 'a')

在Python3中,zip、map和filter对象也是迭代器。

>>> numbers = [1, 2, 3, 5, 7]>>> letters = ['a', 'b', 'c']>>> z = zip(numbers, letters)>>> z>>> next(z)(1, 'a')

Python中的文件对象也是迭代器。

>>> next(open('hello.txt'))'hello worldn'

在Python、标准库和第三方Python库中还有许多内置的迭代器。

至此,已经可以给出完美的解释了。



Tags:Python   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
一篇文章教会你使用Python中三种简单的函数
所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。一、函数简介所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。二、函数定义...【详细内容】
2024-04-11  Search: Python  点击:(7)  评论:(0)  加入收藏
一篇文章带你了解Python的分布式进程接口
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。一、前言在Thread和Process中,应当优...【详细内容】
2024-04-11  Search: Python  点击:(5)  评论:(0)  加入收藏
Python 可视化:Plotly 库使用基础
当使用 Plotly 进行数据可视化时,我们可以通过以下示例展示多种绘图方法,每个示例都会有详细的注释和说明。1.创建折线图import plotly.graph_objects as go# 示例1: 创建简单...【详细内容】
2024-04-01  Search: Python  点击:(10)  评论:(0)  加入收藏
Python 办公神器:教你使用 Python 批量制作 PPT
介绍本文将介绍如何使用openpyxl和pptx库来批量制作PPT奖状。本文假设你已经安装了python和这两个库。本文的场景是:一名基层人员,要给一次比赛活动获奖的500名选手制作奖状,并...【详细内容】
2024-03-26  Search: Python  点击:(21)  评论:(0)  加入收藏
Python实现工厂模式、抽象工厂,单例模式
工厂模式是一种常见的设计模式,它可以帮助我们创建对象的过程更加灵活和可扩展。在Python中,我们可以使用函数和类来实现工厂模式。一、Python中实现工厂模式工厂模式是一种常...【详细内容】
2024-03-07  Search: Python  点击:(35)  评论:(0)  加入收藏
不可不学的Python技巧:字典推导式使用全攻略
Python的字典推导式是一种优雅而强大的工具,用于创建字典(dict)。这种方法不仅代码更加简洁,而且执行效率高。无论你是Python新手还是有经验的开发者,掌握字典推导式都将是你技能...【详细内容】
2024-02-22  Search: Python  点击:(40)  评论:(0)  加入收藏
如何进行Python代码的代码重构和优化?
Python是一种高级编程语言,它具有简洁、易于理解和易于维护的特点。然而,代码重构和优化对于保持代码质量和性能至关重要。什么是代码重构?代码重构是指在不改变代码外部行为的...【详细内容】
2024-02-22  Search: Python  点击:(42)  评论:(0)  加入收藏
Python开发者必备的八个PyCharm插件
在编写代码的过程中,括号几乎无处不在,以至于有时我们会拼命辨别哪个闭合括号与哪个开头的括号相匹配。这款插件能帮助解决这个众所周知的问题。前言在PyCharm中浏览插件列表...【详细内容】
2024-01-26  Search: Python  点击:(91)  评论:(0)  加入收藏
Python的Graphlib库,再也不用手敲图结构了
Python中的graphlib库是一个功能强大且易于使用的工具。graphlib提供了许多功能,可以帮助您创建、操作和分析图形对象。本文将介绍graphlib库的主要用法,并提供一些示例代码和...【详细内容】
2024-01-26  Search: Python  点击:(90)  评论:(0)  加入收藏
大语言模型插件功能在携程的Python实践
作者简介成学,携程高级安全研发工程师,关注Python/Golang后端开发、大语言模型等领域。一、背景2023年初,科技圈最火爆的话题莫过于大语言模型了,它是一种全新的聊天机器人模型,...【详细内容】
2024-01-26  Search: Python  点击:(81)  评论:(0)  加入收藏
▌简易百科推荐
一篇文章教会你使用Python中三种简单的函数
所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。一、函数简介所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。二、函数定义...【详细内容】
2024-04-11  Go语言进阶学习  微信公众号  Tags:Python   点击:(7)  评论:(0)  加入收藏
一篇文章带你了解Python的分布式进程接口
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。一、前言在Thread和Process中,应当优...【详细内容】
2024-04-11  Go语言进阶学习    Tags:Python   点击:(5)  评论:(0)  加入收藏
Python 可视化:Plotly 库使用基础
当使用 Plotly 进行数据可视化时,我们可以通过以下示例展示多种绘图方法,每个示例都会有详细的注释和说明。1.创建折线图import plotly.graph_objects as go# 示例1: 创建简单...【详细内容】
2024-04-01  Python技术    Tags:Python   点击:(10)  评论:(0)  加入收藏
Python 办公神器:教你使用 Python 批量制作 PPT
介绍本文将介绍如何使用openpyxl和pptx库来批量制作PPT奖状。本文假设你已经安装了python和这两个库。本文的场景是:一名基层人员,要给一次比赛活动获奖的500名选手制作奖状,并...【详细内容】
2024-03-26  Python技术  微信公众号  Tags:Python   点击:(21)  评论:(0)  加入收藏
Python实现工厂模式、抽象工厂,单例模式
工厂模式是一种常见的设计模式,它可以帮助我们创建对象的过程更加灵活和可扩展。在Python中,我们可以使用函数和类来实现工厂模式。一、Python中实现工厂模式工厂模式是一种常...【详细内容】
2024-03-07  Python都知道  微信公众号  Tags:Python   点击:(35)  评论:(0)  加入收藏
不可不学的Python技巧:字典推导式使用全攻略
Python的字典推导式是一种优雅而强大的工具,用于创建字典(dict)。这种方法不仅代码更加简洁,而且执行效率高。无论你是Python新手还是有经验的开发者,掌握字典推导式都将是你技能...【详细内容】
2024-02-22  子午Python  微信公众号  Tags:Python技巧   点击:(40)  评论:(0)  加入收藏
如何进行Python代码的代码重构和优化?
Python是一种高级编程语言,它具有简洁、易于理解和易于维护的特点。然而,代码重构和优化对于保持代码质量和性能至关重要。什么是代码重构?代码重构是指在不改变代码外部行为的...【详细内容】
2024-02-22  编程技术汇    Tags:Python代码   点击:(42)  评论:(0)  加入收藏
Python开发者必备的八个PyCharm插件
在编写代码的过程中,括号几乎无处不在,以至于有时我们会拼命辨别哪个闭合括号与哪个开头的括号相匹配。这款插件能帮助解决这个众所周知的问题。前言在PyCharm中浏览插件列表...【详细内容】
2024-01-26  Python学研大本营  微信公众号  Tags:PyCharm插件   点击:(91)  评论:(0)  加入收藏
Python的Graphlib库,再也不用手敲图结构了
Python中的graphlib库是一个功能强大且易于使用的工具。graphlib提供了许多功能,可以帮助您创建、操作和分析图形对象。本文将介绍graphlib库的主要用法,并提供一些示例代码和...【详细内容】
2024-01-26  科学随想录  微信公众号  Tags:Graphlib库   点击:(90)  评论:(0)  加入收藏
Python分布式爬虫打造搜索引擎
简单分布式爬虫结构主从模式是指由一台主机作为控制节点负责所有运行网络爬虫的主机进行管理,爬虫只需要从控制节点那里接收任务,并把新生成任务提交给控制节点就可以了,在这个...【详细内容】
2024-01-25  大雷家吃饭    Tags:Python   点击:(61)  评论:(0)  加入收藏
站内最新
站内热门
站内头条