python小小白

Python进阶|列表生成式、列表生成器

2020-01-02  本文已影响0人  凡有言说

列表生成式

列表生成式是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

当我们去遍历它的时候,生成器会通过特定的算法不断的推断出相应的元素,边运行边推算结果,节省空间。

参考资料:
列表生成式
生成器
yield和生成器

公众号.png
上一篇下一篇

猜你喜欢

热点阅读