Ⅰ起因

  学习python的同学通常会遇到这样一道经典生成器测试题:

def gen():
for i in range(4):
yield i
base = gen()
for n in (2,10):
base = (i+n for i in base)
print(list(base))
[21,22,23,24]

#简单解答:
因为for循环了两次,并对base从新赋值了,所以可以简化为(i+n for i in (i+n for i in base)) 而n 全部引用了后赋值的10。最里面的base引用的是gen。

答案及解释

   但是这个解答并没有回答一个核心问题:为什么最里层的n 始终用的是10,而base可以找到之前的gen()?

     为了简化问题,我把这道题改造了成这样:

a1 = 3
b = (i for i in range(a1))
a1 = 5
list(b) #[0, 1, 2] a1 = 3
b = (a1 for i in range(3))
a1= 5
list(b) #[5, 5, 5]

  或许各位会猜测:这个问题可能和for后面的数据类型有关系吧?

Ⅱ原理探索

  但如果把range()和前面的数值都改造为列表,结果如下:

a1 = 1
b=([a1,] for i in range(3))
a1=2
list(b) # [[2], [2], [2]] a1 =1
b = (i for i in [a1,])
a1 = 2
list(b) # [1] #也可以把以上两个表达式结合一下
a1 = 1
b = ([a1,i] for i in [a1,])
a1 = 2
list(b) #[[2, 1]]

显而易见,当变量在for前面的时候,会引用后声明的值,而当变量在for后面的iterator中的时候会引用之前声明的值,并且与数据类型无关。

By the way, 可能很人多还不确定列表本身的设定:a =1     b = [a,]       a =2     print(b)   #[1,]

当然以上全部是生成器表达式。如果手动定义一下生成器呢?

a =1
def zz():
for i in [a,]:
yield [a,i]
cc = zz()
a=2
print(list(cc)) #[[2, 2]] #如果传入a
a =1
def zz(a):
for i in [a,]:
yield [a,i]
cc = zz(a)
a=2
print(list(cc)) #[[1,1]]

  生成器函数的测试结果是前后一致,不存在这个问题。

  进一步测试:

a = 1
c = ([b,i] for i in [a,])
b = 1
list(c) #[[1, 1]] # 但是如果a在生成器表达式后面定义的话:
c = ([b,i] for i in [a,])
b = 1
a = 1
list(c) # 会报错 #p.s. 在生成器函数也不会报错

Ⅲ执行效率比较

  对于简单的生成器,生成器表达式更方便、更直观。那么两者的执行效率是否存在差异呢?Timeit!

import timeit
def b():
a = 9999
def c():
for i in range(a):
yield i
list(c())
print(timeit.timeit(stmt=b,number=1000))

函数模式

import timeit
def b():
a = 9999
c = (i for i in range(a))
list(c)
print(timeit.timeit(stmt=b,number=1000))

表达式

结果:

    函数模式    表达式模式

    1.260876    1.235369  
    1.253225    1.238639
    1.256804    1.235393
    1.258575    1.238165

我们看到生成器表达式提供的便利的确是以效率的损耗作为代价的。

进一步的验证表明:生成器表达式初始化的过程相比生成器函数需要花费更多的时间(接近2倍),但是由于初始化的时间过短,并不是形成差距的主要原因。函数模式的生成器会随着next()次数的增加在时间上逐步拉开与生成器表达式差距。调用效率的差距才是主要原因。

Ⅳ结论

  生成器表达式,会在程序执行的过程中运行for 后面的代码,并对for后面的代码进行赋值,而for之前的代码以及生成器函数并不会执行,只会进行编译。

  尽管,生成器表达式代码更简洁,但在生成器初始化和生成器调用的效率上都表现出了与传统生成器函数的差距。

注:列表推导式并不存在这样的问题(当然也不应该出现)

python 特别的生成器表达式的更多相关文章

  1. 05.python解析式与生成器表达式

    解析式和生成器表达式 列表解析式 列表解析式List Comprehension,也叫列表推导式 #生成一个列表,元素0-9,将每个元素加1后的平方值组成新的列表 x = [] for i in ra ...

  2. Python - 列表解析式/生成器表达式

    列表解析式: [expr for iter_var in iterable if cond_expr] 生成器表达式: (expr for iter_var in iterable if cond_e ...

  3. 详解Python中的生成器表达式(generator expression)

    介绍 1.生成器表达式(generator expression)也叫生成器推导式或生成器解析式,用法与列表推导式非常相似,在形式上生成器推导式使用圆括号(parentheses)作为定界符,而不是列 ...

  4. python基础之生成器表达式形式、面向过程编程、内置函数部分

    生成器表达式形式 直接上代码 1 # yield的表达式形式 2 def foo(): 3 print('starting') 4 while True: 5 x=yield #默认返回为空,实际上为 ...

  5. 详解python中的生成器表达式

    什么是生成器表达式 还记得列表解析吗?我们把[]换成()就变成生成器表达式了. g = (x for x in [1, 2, 3, 4]) print(g) # <generator objec ...

  6. 【Python】【容器 | 迭代对象 | 迭代器 | 生成器 | 生成器表达式 | 协程 | 期物 | 任务】

    Python 的 asyncio 类似于 C++ 的 Boost.Asio. 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知. Asyn ...

  7. Python 进阶_生成器 & 生成器表达式

    目录 目录 相关知识点 生成器 生成器 fab 的执行过程 生成器和迭代器的区别 生成器的优势 加强的生成器特性 生成器表达式 生成器表达式样例 小结 相关知识点 Python 进阶_迭代器 & ...

  8. python迭代器和生成器(3元运算,列表生成式,生成器表达式,生成器函数)

    1.1迭代器 什么是迭代器: 迭代器是一个可以记住遍历的位置对象 迭代器对象从集合的第一个元素元素开始访问,直到所有元素被访问完结束,迭代器只能往前不会后退. 迭代器有两个基本方法:iter ,nex ...

  9. Python学习笔记2:构造序列:列表推导和生成器表达式

    欢迎访问个人网站:www.comingnext.cn 1. 关于Python内置序列类型 a. 按能否存放不同类型的数据区分 容器序列: list.tuple 和collections.deque这些 ...

随机推荐

  1. ActionScript3.0(AS3)中的泛型数组Vector

    Adobe官方并没有"泛型数组"的叫法,这是我自己对Vector的叫法(有点标题党),不过Vector在使用上确实跟c#中的泛型数组有些相似之处. 原作者:菩提树下的杨过出处:ht ...

  2. Python学习—数据库篇之初识mysql

    一.下载与安装 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司.MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好 ...

  3. Linux-03

    目录处理命令 目录处理命令:ls 命令名称:ls 命令英文原意:list 命令所在路径:/bin/ls 执行权限:所有用户 功能描述:现实目录文件 语法:ls 选项[-ald] [文件或目录] -a ...

  4. 一段自适应的CSS代码

    一段自适应HTML5的CSS代码,该代码在陕西特产使用过,手机端效果还好,就是PC端看起来没那么大气,比较窄屏 * { transition-property: all; -ms-transition ...

  5. vue动态绑定background:url绑不上的问题

    场景: 利用swipper做轮播图,在联调的时候发现有些图片存在有些图片不存在 原因:图片路径中存在 (),和 background:url() 会冲突 解决方法: 一:oss图片路径避免出现括号 ( ...

  6. 设计模式之jdk动态代理模式、责任链模式-java实现

    设计模式之JDK动态代理模式.责任链模式 需求场景 当我们的代码中的类随着业务量的增大而不断增大仿佛没有尽头时,我们可以考虑使用动态代理设计模式,代理类的代码量被固定下来,不会随着业务量的增大而增大. ...

  7. 发布后就很丢人的随笔(c++)

    for循环条件中使用的变量 可以是循环条件中定义的变量 也可以是循环之外的变量 代码: #include <iostream> int main(){ int a = 1; int b = ...

  8. centos 7 常用工具篇

    1.vmstat监控linux整体性能工具如查看命令:vmstat 1 4 2.用netstat查看网络连接,接口等信息如:netstat -an 3.dig  或者nslookup 跟踪yum解析过 ...

  9. Redis5种常用的数据结构

    一.数据结构 五种常用的数据结构:string.hash.list.set.zse,以及三种不常用的:hyperloglog.geospatial.streams. 二.常用数据结构的使用 1.Str ...

  10. CSS3网页动画

    CSS3网页动画 概要:CSS3变形是一些效果的集合 如:平移.旋转.缩放.倾斜效果 每个效果都可以称为变形(transform)他们可以分别操控元素发生平移.旋转.缩放.倾斜等变化. 网页中能够实现 ...