我是靠谱客的博主 文静宝贝,最近开发中收集的这篇文章主要介绍js如何提高for循环的效率_技术探讨贴:生成式与for循环是否等价?—— Python提高篇第十五回释疑...,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

大家好,今天我们发布了《全民一起玩Python 提高篇》第十五回“字典也有生成式,却拿空间换时间”,重点介绍了将列表转换为字典的三种方法(dict fromkeys 字典生成式),并剖析了一个常见的数据分析需求:怎样使用字典进行频次统计。而且在课程中,我们使用上述三种字典创建方法分别实现了这一功能,以便大家能够深入理解三种方法的区别。

9bf44107a300683a4cac473628eaf112.png

在这三种方法中,最灵活的当属 “字典生成式” ,就像上一节课讲的 “列表生成式” 一样,都可以看做对 for 循环的语法简化。事实上,很多资料在讲解这一内容时也都会说 “ (列表或字典)生成式等价于 for 循环”,然而正如本节课末尾杨老师提到的:如果严格推敲,“等价”二字恐怕不能成立。由于杨老师在网络上还没有找到过详细阐述这个问题的文章,所以通过本文简单为同学分析一下。

我们已经学过,只要按照下面的规则,就可以把一个使用 for 循环创建列表的代码改造成一个生成式:

  1. 将循环体内 append() 方法的参数放到前面;

  2. 将循环规则(for语句)写到后面;

  3. 套上方括号。

比如下面这段代码,就是一个典型的从 for 到 生成式的例子 :

7939b13e1e258d3e62dddee3f212d37e.png

其对应的转换过程如下图所示:

3a7d5cd2643c3959091ec329d1e5ffcf.png

上图中左右两段代码运行效果完全相同,但是仔细思考就会发现:它们的执行流程其实存在一个重要的差别:是先创建列表再逐个添加元素,还是先算出所有元素数值、然后再创建列表并把它们放进去?

具体来说,在 for 循环的模式中,我们会先创建一个空列表,然后再进入循环向其中逐个添加元素。比如在上面的左边的 for 模式代码里,程序进入 for 循环之前首先执行了一句 a=[ ] ,因而在循环开始时,内存中就已经存在一个列表,并且由变量 a 指向它。而接下来在每次循环中,每次执行append 方法都是在直接修改这个列表 a 。

而在生成式中,Python 则是通过方括号内的循环语句把所有元素数值都计算出来,然后再创建一个空列表,最后把算好的那些数值一次性存入到列表中。比如上图右侧的代码,实际执行的过程是先算出 0、1、4、9、…… 81 等10个数字放在内存里,然后再创建空列表 a ,最后把这10个数字作为10个元素添加到 a 中。

换句话说:在 for 循环的模式里,进入循环时最终要创建的列表就已经存在,所以循环时就可以调用这个新列表;而在生成式模式里,直到循环结束时,最终的列表才会被创建,所以在循环过程中完全不能调用这个新列表。

这一点区别并不会影响大多数日常程序。但是如果我们设计一个特殊的案例,需要在循环生成每个元素时就调用新列表,那么问题就来了。比如下面的 for 模式就在循环时调用了新创建的列表,用 len 函数计算它的长度。所以添加的第一个元素数值为当时列表 a 的长度(也就是 0,因为 a 还是一个空列表)、第二个元素数值为第二次循环时的列表长度(此时为 1 ,因为上一次循环后列表 a 中已经多了一个元素)…… 因此循环5次、添加5个元素后,列表为 [ 0, 1, 2, 3, 4 ] :

fefdd4d33ff60dd9a52f6eea7186249d.png

但是如果我们把这个for循环改成生成式,马上就会执行出错,因为在循环调用 len(a) 时,a 列表 还没有被创建出来,所以说 “name a is not defined”。

42d039aaf169ace003314aba6729717b.png

那么我们可不可以也像 for 那样,在生成式之前先定义一下  a=[] 呢?这样做确实可以,而且避免了前面那个 “名字 a 未定义” 的错误。但是程序执行的结果却仍然与 for 不同,是一个全零列表 [ 0, 0, 0, 0, 0 ] :

2ca20e9f20202ac965c7e3cd8ed1e22b.png

这一回为什么全是 0 呢?答案就在前面所说的规则里:生成式在每一次循环时都还没有创建新列表,所以每次 len(a) 都是在计算之前定义的空列表 a 的长度,于是得到的都是 0 。接下来,当5次循环结束后,python 才创建了一个新的列表,并一次性给它添加了 5 个元素,数值都是刚才算出来的那些 0 。最后根据赋值语句的规则,将这个包含 5 个 0 的新列表赋值给变量 a ,于是 a 不再指向之前定义的那个空列表,而是转向这个全零列表。

由此可见,一旦我们需要在循环中调用新创建的列表(以及字典等),for循环和生成式两种方法就会出现差异。当然,上面举的这个例子没有任何实际意义,只是用来演示这种差异的存在。但是有时候我们却会在编写一些实际功能时也掉入这个陷阱,比如我们在本节课程最后演示的这个字典的例子:

41d5aa3faba7068669642535649217c8.png

这个例子尝试统计列表 a 中各个字符的出现次数,并将结果存入字典。但是左边 for 循环的模式可以正常完成任务,而右边同样语法的生成式却会计算出错。原因就在于:生成式里用到的字典 d ,并不是随时添加新元素的那个新字典,而是前一行定义的那个默认值全是 0 的字典。具体过程,请有兴趣的读者自己逐步分析一下就好。

总之,Python提供的容器类型非常强大,特别适合于数据统计和分析。但是归根到底,Python不是计算器而是一门程序语言,所以如果我们不从计算机的角度去理解它、而只把它看做一个“傻瓜式”的工具,那么即使把生成式这样的技巧玩的再熟练,也还是会被这种莫名其妙的问题坑到。所以还是那句话:编程思维最重要!

最后

以上就是文静宝贝为你收集整理的js如何提高for循环的效率_技术探讨贴:生成式与for循环是否等价?—— Python提高篇第十五回释疑...的全部内容,希望文章能够帮你解决js如何提高for循环的效率_技术探讨贴:生成式与for循环是否等价?—— Python提高篇第十五回释疑...所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部