Python语言 循环中的陷阱
小标 2018-11-14 来源 : 阅读 941 评论 0

摘要:本文主要向大家介绍了Python语言 循环中的陷阱,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助。

本文主要向大家介绍了Python语言 循环中的陷阱,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助。

Python 中的 for 循环和其他语言中的 for 循环工作方式是不一样的,今天就带你深入了解 Python 的 for 循环,看看它是如何工作的,以及它为什么按照这种方式工作。循环中的陷阱我们先来看一下 Python 循环中的「陷阱」,在我们了解了循环的工作方式后,再来看下这些陷阱到底是怎么出现的。陷阱 1:循环两次现在我们先假设有一个数字组成的列表,和一个用于返回这些数字的平方的生成器:>>> nums = [1, 2, 3, 4]>>> squares = (n**2 for n in nums)我们可以将这个生成器对象传递给元组构造器,从而可以得到一个元组:>>> tuple(squares)
(1, 4, 9, 16)这个时候,如果我们再将这个构造器对象传递给 sum 函数,按理说应该会返回这些数字的和吧:>>> sum(squares)0返回的是个 0,先拖住下巴。陷阱 2:检查是否包含我们还是使用上面的数字列表和生成器:>>> nums = [1, 2, 3, 4]>>> squares = (n**2 for n in nums)如果我 squares 生成器中是否包含 9,答案是肯定的,若果我再问一次呢?你敢答应吗>>> 9 in squaresTrue>>> 9 in squaresFalse发现,第二次不灵了~怎么不灵了陷阱 3:拆包现在假设有一个字典:>>> counts = {1:'a', 2:'b'}然后,我们用多个变量对字典进行拆包:>>> x,y = counts你觉得这时候,x 和 y 中会是什么?>>> x1>>> y2我们只得到了键。下面,我们先来了解下 Python 中的循环工作原理,然后再反过头来看这些陷阱问题。一些概念首先,先了解一些基本概念:可迭代和序列可迭代就是指任意可以使用 for 循环遍历的东西,可迭代意味着可以遍历,任何可以遍历的东西都是可迭代的。for item in some_iterable:
    print(item)序列是一种常见的可迭代类型,如列表、元组、字符串等。序列是可迭代的,它有着一些特点,它们是从 0 开始索引,索引长度不超过序列的长度;它们有序列长度;并且它们可以被切分。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 还有着许多其他种类的可迭代类型。迭代器迭代器就是可以驱动可迭代对象的东西。你可以从任何可迭代对象中获得迭代器,你也可以使用迭代器来手动对它的迭代进行遍历。下面有三个可迭代对象:一个集合、一个元祖和一个字符串:>>> nums = {1,2,3,4}>>> coors = (4,5,6)>>> words = "hello hoxis"我们可以使用 Python 的内置函数 iter ,从这些可迭代对象中获取到迭代器:>>> iter(nums)
>>> iter(coors)
>>> iter(words)
一旦我们有了迭代器,我们就可以使用其内置函数  next() 来获取它的下一个值:>>> nums = {1,2,3,4}>>> num_iter = iter(nums)>>> next(num_iter)1>>> next(num_iter)2>>> next(num_iter)3>>> next(num_iter)4>>> next(num_iter)
Traceback (most recent call last):
  File "", line 1, in StopIteration若果迭代到头了,也就是没有下一个值了,就会抛出 StopIteration 异常。也就是说,它不会继续循环取获取第一个值。是不是有点懵逼了?可迭代对象是可以迭代的东西迭代对象器实际上是遍历可迭代对象的代理迭代器没有长度,它们不能被索引。可以使用迭代器来做的唯一有用的事情是将其传递给内置的 next 函数,或者对其进行循环遍历可以使用 list() 函数将迭代器转换为列表>>> nums = {1,2,3,4}>>> num_iter = iter(nums)>>> next(num_iter)1>>> list(num_iter)
[2, 3, 4]>>> list(num_iter)
[]若果想再次将其转换为列表,明显地,得到的是一个空列表。其实这也是迭代器的一个重要特性:惰性,只能使用一次,只能循环遍历一次。并且,在我们调用 next() 函数之前,它不会做任何事情。因此,我们可以创建无限长的迭代器,而创建无限长的列表则不行,那样会耗尽你的内存!可迭代对象不一定是迭代器,但是迭代器一定是可迭代的:对象可迭代?迭代器?可迭代对象√不一定迭代器√√生成器√√列表√×其实,Python 中有许多迭代器,生成器是迭代器,Python 的许多内置类型也是迭代器。例如,Python 的 enumerate 和 reversed 对象就是迭代器。zip, map 和 filter 也是迭代器;文件对象也是迭代器。Python 中的 for 循环其实,Python 并没有传统的 for 循环,什么是传统的 for 循环?我们看下 Java 中的 for 循环:int[] integers = {1, 2, 3, 4};for (int j = 0; j<integers.length; j++) {    int i = integers[j];
    System.out.println(i);
}这是一种 C风格 的 for 循环,JavaScript、C、C++、Java、PHP 和一大堆其他编程语言都有这种风格的 for 循环,但是 Python 确实没有。Python 中的我们称之为 for 循环的东西,确切的说应该是 foreach 循环:numbers = [1, 2, 3, 5, 7]for n in numbers:
    print(n)和 C风格 的 for 循环不同之处在于,Python 的 for 循环没有索引变量,没有索引变量的初始化,边界检查和索引变量的增长。这就是 Python 的 for 循环的不同之处!使用索引?你可能会怀疑,Python 的 for 循环是否在底层使用了索引,下面我们手动的使用 while 循环和索引来遍历:>>> nums = [1,2,3,4]>>> i = 0>>> while i < len(nums):...     print(num[i])...     i += 1...0123对于列表,这样遍历是可以的,但不代表适用于所有可迭代对象,它只适用于序列。比如,我们对一个 set 使用这种方法遍历,会得到一个异常:>>> set = {1,2,3}>>> i = 0>>> while i < len(set):...     print(set[i])...     i += 1...
Traceback (most recent call last):
  File "", line 2, in 
TypeError: 'set' object does not support indexing因为 set 不是序列,因此不支持索引遍历。我们不能使用索引手动对 Python 中的每一个迭代对象进行遍历。对于那些不是序列的迭代器来说,更是行不通的。实现没有 for 的循环从上文可以看出,Python 中的 for 循环不使用索引,它使用的是迭代器。让我们来看下它是如何工作的。通过上文,我们了解到了迭代器和 iter、next 函数,现在我们可以尝试不用 for 循环来遍历一个可迭代对象。下面是一个正常的 for 循环:def funky_for_loop(iterable, action_to_do):
    for item in iterable:
        action_to_do(item)我们要尝试用迭代器的方法和 while 实现上面 for 循环的逻辑,大致步骤如下:获取给定可迭代对象的迭代器;调用迭代器的 next() 方法获取下一项;对当前项数据进行处理;如果捕获到 StopIteration ,那么就停止循环def funky_for_loop(iterable, action_to_do):
    iterator = iter(iterable)    while not done_looping:        try:
            item = next(iterator)        except StopIteration:            break
        else:
            action_to_do(item)Python 底层的循环工作方式基本上如上代码,就是迭代器驱动的 for 循环。再次回到循环陷阱陷阱 1:耗尽的迭代器陷阱 1 中,因为生成器是迭代器,迭代器是惰性的,也是一次性的,在已经遍历过一次的情况下,再对其求和,返回的就是一个 0。陷阱 2:部分消耗迭代器陷阱 2 中,我们两次询问 9 是否存在于同一个生成器中,得到了不同的答案。这是因为,第一次询问时,Python 已经对这个生成器进行了遍历,也就是调用 next() 函数查找 9,找到后就会返回 True,第二次再询问 9 是否存在时,会从上次的位置继续 next() 查找。>>> nums = [1,2,3,4,5]>>> squares = (n**2 for n in nums)>>> 9 in squaresTrue# 此时打印出来>>> list(squares)
[16, 25]陷阱 3:拆包是迭代当直接在字典上迭代时,得到的是键:>>> counts = {1:'a',2:'b'}>>> for i in counts:...     print(i)... 12而对字典拆包时,和在字典上遍历是一样的,都是依赖于迭代器协议,因此得到的也是键。总结序列是迭代器,但是不是所有的迭代器都是序列。迭代器不可以被循环遍历两次、不能访问其长度,也不能使用索引。迭代器是 Python 中最基本的可迭代形式。如果你想在代码中做一个惰性迭代,请考虑迭代器,并考虑使用生成器函数或生成器表达式。最后,请记住,Python 中的每一种迭代都依赖于迭代器协议,因此理解迭代器协议是理解 Python 中的循环的关键。

本文由职坐标整理并发布,希望对同学们学习Python有所帮助,更多内容请关注职坐标编程语言Python频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程