生成器、迭代器作为python的两个高级特性,相信大家肯定耳熟能详,都能说道上一阵,但很多时候都是说说而已,知道有这么个东西,而且是好东西,但再看看写过的代码,有多少确实使用它的?
一个语音特性,在潜意识中没用被激活,更多时候还是因为不知道它的应用场景,这里就从三个方面说说生成器什么时候应该用,而且必须用。
是否需要返回列表中的所有元素?
不需要
当做出不需要的回答时,就应该选择生成器,而不是列表,因为生成器的主要特性就是'lazy evaluation'
生成器,只有在真正需要时才生成结果,因此在不需要列表中的所有元素,自然是没必要去创建它们的,创建后还不用,那就是浪费资源,而且影响效率
例如,我们碰到如下场景,公司年会搞一个抽奖活动,规则是每人拿一个号,抽奖时根据这些号码摇号,摇出的号和你对应的号一致,那就表示中奖。
下面给出一个简单实现:
import time
import random
def gen_winning_numbers():
random.seed()
elements = []
for i in range (0,10):
time.sleep(1) # 模拟很牛逼的摇号算法但有些费时
elements.append(random.randint(1,10))
return elements
random.seed()
my_number = random.randint(1,10)
print ("my number is " + str(my_number))
for winning_number in gen_winning_numbers():
print(winning_number)
if my_number == winning_number:
print ("you win!")
break
gen_winning_numbers函数是一个比较耗时的函数,随机产生10个中奖号码,如果my_number在这10个数中,表示中奖且程序退出,从这个实现中可以看到,不管如何至少需要等10s,才知道中奖结果。
而往往只要有一个中奖号码和my_number一致,就表示中奖,就无需关心其他中奖号码,也没必要生成其他剩余的号码,最优情况下,只需要1s就得到中奖结果了
使用生成器就很容易解决这个问题
import time
import random
def gen_winning_numbers():
random.seed()
for i in range(0,10):
time.sleep(1) # 模拟很牛逼的摇号算法但有些耗时
yield random.randint(1,10)
random.seed()
my_number = random.randint(1,10)
print ("my number is " + str(my_number))
for winning_number in gen_winning_numbers():
print(winning_number)
if my_number == winning_number:
print ("you win!")
break
函数是否需要大内存?
需要
当做出需要的回答时,就应该选择生成器,因为生成器在需要时创建,获取到结果时才开始处理,完成后在请求其他项目前可从内存中删除,释放内存
先看看下面这段代码
def get_elements():
elements = []
for i in range (0,10000):
elements.append("x"*10240)
# 返回1W个每个元素10KB的大列表
return elements
characters_count = 0
my_elements=get_elements()
for i in my_elements:
characters_count = characters_count + len(i)
print(characters_count)
这段代码每次执行时至少需要占用超过100M的内存,而如果使用生成器,可是另外一番景象
def get_elements():
for i in range (0,10000):
yield("x"*10240)
characters_count = 0
my_elements=get_elements()
for i in my_elements:
characters_count = characters_count + len(i)
print(characters_count)
得到同样的结果,但只需要10KB的内存
列表生成过程中是否需要通知?
需要
当做出需要的回答时,就应该选择生成器
在一个复杂或是耗时相对较长的列表生成过程中,用户如果不知道当前的元素过程,一味的盲目等待,那应该是很烦人的、无法接受的。
例如,下面这个过程
import time
def elements():
elements = []
for i in range (0,4):
# 模拟耗时操作
time.sleep(5)
elements.append(i)
return elements
print("start")
print(elements())
print("end")
每次需要得到5个结果,必须得等待20s,更糟糕的是,在等待的过程中什么都做不了,也不知内部具体情况,哪怕给个进度提示也好
import time
def elements():
elements = []
for i in range (0,4):
# 模拟耗时操作
time.sleep(5)
yield(i)
print("start")
for i in elements():
# 输出一个简易进度
print(".", end="", flush=True)
print()
print("end")
由上可知:
如果创建一个元素的函数很耗时,如果该函数对内存占用敏感,或是不需要列表中的所有元素,那么最佳选择是生成器,那其他情况下,都可以使用列表,对吧?
当然没问题,但是如果选择生成器是否更好呢,既拥有了上述优势,也不乏列表的熟悉味道,因为生成器转换成列表很简单
1. list方法
mylist = list(my_generator())
2. 列表生成式
mylist = [elem for elem in my_generator()]