列表生成式
列表生成式是Python内置的非常简单却强大的可以用来创建list的生成式。
list1 = [i for i in range(10)]
list1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
比如像获取平方数,若使用常规写法:
list2 = []
for x in range(10):
list2.append(x*x)
list2
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
若使用列表生成式
list2 = [x*x for x in range(10)]
list2
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
当然我们也可以在列表生成式里加入判断条件,比如获取偶数的平方:
list3 = [x*x for x in range(10) if x%2 == 0]
list3
[0, 4, 16, 36, 64]
此外,也可以做嵌套循环,比如
list4 = [x+y for x in "ABC" for y in "XYZ"]
list4
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
列表生成器
通过上面的列表生成式,我们可以直接创建一个列表。如果此时想创建一个几百万个元素的列表,这种方式就不大可取。一是占用内存多,二是如果仅访问前面若干元素,那该列表占用的内存空间就浪费了。
此时,若列表元素可以按照某种算法推算出来,那就可以在循环的过程中不断推算出后续的元素。幸运的是,在Python中有这种一边循环一边计算的机制,它称为生成器。
list51 = [i for i in range(10)]
list52 = (i for i in range(10))
print(list51)
print(list52)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<generator object <genexpr> at 0x000002877DF0F5E8>
我们可以通过 next()
来调用这个生成器里面的元素
next(list52)
0
next(list52)
1
我们可以对比下生成式和生成器的耗时,比如我们像生成1亿个元素:
#计算时间
import time
def func_time(func):
def wrapper():
start_time = time.perf_counter()
my_func = func()
end_time = time.perf_counter()
print("方法{}消耗了{}ms".format(func.__name__, (end_time-start_time)*1000))
return my_func
return wrapper
@func_time
def calculate_func_1():
list1 = [i for i in range(100000000)]
@func_time
def calculate_func_2():
list2 = (i for i in range(100000000))
calculate_func_1()
calculate_func_2()
结果是:
方法calculate_func_1消耗了13234.298399999943ms
方法calculate_func_2消耗了0.011099999937869143ms
可以看出,生成器生成的列表 list2 所消耗的时间远低于 list1。这是因为用列表生成式的列表是固定的,即list1中封装的元素格式就是1亿个。
而lis2则是记录了一定的算法规则,比如上例中的+=i
,生成器不需要把数据全部装进list2,而是等到我们需要的时候,算一下然后把数据返回给我们,从而节省了空间。
最后来了解下yield
,它和return
有点像。对于return
,只要执行了到它,就会返回相应的结果并且终止函数的执行:
def add():
a = 1
b = 2
return a + b
print("ABC")
add()
3
def foo():
print("a")
yield
print("b")
yield
print("c")
foo()
<generator object foo at 0x000002877DF0F4F8>
没看到返回值,但发现了生成器的身影。原来,一个函数里如果被定义了yield
,那么这个函数就是一个生成器。
def foo():
print("a")
yield
print("b")
yield
print("c")
f = foo()
next(f)
a
第一次调用时,遇到第一个yield
就跳出了函数。
next(f)
b
第二次调用时,就从第一个yield
开始,遇到第二个yield
跳出函数。
我们可以对生成器进行遍历
def foo():
for i in range(5):
yield i*2
for i in foo():
print(i)
0
2
4
6
8
当我们去遍历它的时候,生成器会通过特定的算法不断的推断出相应的元素,边运行边推算结果,节省空间。
公众号.png
网友评论