python 生成器和迭代器通俗理解 python生成器表达式( 二 )


实际上背后就是一个类,所以它有状态 。上一节中我们说过,就是/类的一种简单写法 。
除了函数的写法之外,还可以用表达式的写法 。它的写法和列表推导式类似,区别就是把中括号**[...]「改成小括号」(...)** 。
这是一个列表推导式:
sys
# 生成1到1万的数字的平方
= [i * 2 for i in range(10000)]
这是表达式:
sys
# 生成1到1万的数字的平方
= (i * 2 for i in range(10000))
前者会在内存中生成10000个数字,放在列表中 。
后者不会马上生成,当你每次用next()函数去调用它的时候,它会生成一个并返回 。
如果你对推导式不熟悉,请看终结者系列的另一篇文章 。链接见文末 。
因为可以被循环,经常被拿来和list做对比 。它最让人津津乐道的是它的性能优越性 。
假如你开了一家汉堡店,有个大客户向你订购1000万个汉堡 。你会一次性生产完这些汉堡吗?
傻的汉堡店主会这样:
一次性生产完1000万个汉堡,可是店里根本放不下啊 。再租个仓库放 。可是后来发现汉堡都坏掉了 。
聪明的汉堡店主会这样:
分批生产!客户什么时候来要,就给他们马上生产一批,既不会把店占满,汉堡还新鲜!

python 生成器和迭代器通俗理解  python生成器表达式

文章插图
那如果有个需求,让你生成100亿个随机数,再求和 。你会这样写吗?
# 请不要尝试下面的代码,因为你的电脑可能会卡死!!!
= []
for i in range(1, ):
.(.(1, 100))
print(sum())
如果这样写,你的程序会在内存中生成100亿个整数,这也许会占满你的内存 。
正确的写法是使用,就用我们上面的吧:
print(sum(()))
前面使用list的时候,要先在内存中生成100亿个数字,然后再求和,这占空间又费时间 。
而用是每次用到的时候才生成1个,不用那么多空间 。
我们可以测试一下前面的推导式的例子中占用的内存情况:
>>>sys
>>>= [i * 2 for i in range(10000)]
>>> sys.()
87624 # 列表推导式占用了87624字节的内存
>>>= (i ** 2 for i in range(10000))
>>> print(sys.())
120#只占用了87624字节的内存
这个例子中只生成10000个数字,区别还没那么大 。如果是生成100亿个数字,区别会更大,因为占用的内存基本是恒定的,和数字多少无关 。
如果你曾经在写代码的时候犯了「傻汉堡店主」的问题,那么不要羞愧,因为语言的设计者们都犯过这样的错误!
在 2中很多标准库使用列表形式,出现内存问题 。所以在 3中很多标准库都改用了 。
比如:
range()函数在 2中返回的是一个列表,在 3中返回的是一个 。
字符串的迭代器也是一个
print(iter('终结者2'))
print(iter(range(1, 10000)))
打印结果:
我们再来多看几个代码例子,有的很简单,目的是为了给你增加更多的代码感觉 。
range是一个,所以多大的range内存都不会爆for i in range(5):
print(i)
三次方生成器def (n):
for i in range(1, n, 2):
yield i**3
表达式形式的三次方生成器 = (i**3 for i in range(1,10,2))
不只是用在for循环中,我们可以手动用next()函数调用它def (n):
for i in range(1, n, 2):
yield i * (i + 1)
= (6)
next()
2
next()
12
next()
30
next()
>error
普通的执行到yield就暂停,可以返回一个值或者不返回 。
除了可以返回值,它还可以接收调用者传值进来,这就要使用send()方法 。
除了send()方法,还有,close()方法 。