EfonMark

一番码客 : 挖掘你关心的亮点。
http://www.efonmark.com

本文目录:

[TOC]

image-20200508225219481

先看几个例子。

例子一:生成一个1~10的整数列表

1
list = [1,2,3,4,5,6,7,8,9,10]

或者,

1
list = range(1,11)

例子二:生成一个1~10的每个整数的平方数的列表

1
list = []
2
for num in range(1, 11):
3
    list.append(num*num)

虽然确实是实现了预期的需求,但是需要通过多行代码才能实现,过程非常繁琐,一点都不pythonic。

为了实现类似需求,python提供了一些高级特性来简化,就是列表推导式。

列表推导式

列表推导式(List Comprehensions)是python内置的非常简单且强大的可以用来轻松创建列表(List)的方法。

例子三:用列表推导式创建1~10的所有整数的平方数的列表

1
>>> list = [num*num for num in range(1,11)]
2
>>> list
3
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

可见,列表推导式的书写规则是,生成的元素放在语句前面,紧跟着的是for循环。

而且,在列表推导式的循环后面加上条件判断 。

列子四:创建1~10的所有偶数平方数的列表

1
>>> list = [num * num for num in range(1,11) if num%2 == 0]
2
>>> list
3
[4, 16, 36, 64, 100]

例子五:使用两个for循环将两个列表中的元素进行全排列,继而生成新的列表

1
>>> list = [char+num for char in ['a', 'b', 'c'] for num in ['1','2', '3']]
2
>>> list
3
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']

生成器表达式

基于列表继续扩展,如果列表元素的个数持续扩大,达到10万个,如此大的列表将占据巨大的内存。假设程序实际上只需要访问列表中其中的几个元素,那么列表占用的绝大多数内存空间都是多余的,纯属浪费。Python为了解决这个问题,引入生成器(generator)。

可以通过生成器表达式将列表改为一个生成器。 列表一旦被创建,其包含的元素就实实在在地存在内存中,占据着内存空间,列表存放的是元素本身,而生成器存放的是算法,通过next()调用算法实时生成元素,因此生成器占用的内存空间很小。

将列表推导式的方括号[]改为小括号()即可创建一个生成器。

例子六:

1
>>> g = (num * num for num in range(1,6))
2
>>> g
3
<generator object <genexpr> at 0x0000018E0754F228>
4
>>> next(g)
5
1
6
>>> next(g)
7
4
8
>>> next(g)
9
9
10
>>> next(g)
11
16
12
>>> next(g)
13
25
14
>>> next(g)
15
Traceback (most recent call last):
16
  File "<stdin>", line 1, in <module>
17
StopIteration

说明:可以看到,列表list一旦被创建,内存中就存放所有元素,而生成器g的元素内容将随着next()函数的调用实时生成,直到最后没有元素可生成时,抛出StopIteration错误。

生成函数器

除了表达式可以变为生成器之外,函数同样可以变为生成器。

例子七:编写一个函数,通过该函数可以计算出任意自然数的平方数。

1
#!usr/bin/env python
2
def square(input):
3
    list = []
4
    for num in range(input):
5
        list.append(num * num)
6
        print(list)
7
    return list
8
9
for num in square(5):
10
    print(num)

结果:

1
[0]
2
[0, 1]
3
[0, 1, 4]
4
[0, 1, 4, 9]
5
[0, 1, 4, 9, 16]
6
0
7
1
8
4
9
9
10
16

说明:可以看到square()函数在运行过程中会创建一个列表,假设想要计算的自然数变大,那么列表也会变大,此程序便会占据大量的内存,当自然数无限大时,程序将因无法分配到足够的内存而崩溃。

例子八:借助生成器,将square()函数改为生成器函数。

1
#!usr/bin/env python
2
def square(input):
3
    for num in range(input):
4
        print('before yield')
5
        yield num * num
6
        print('after yield')
7
8
for num in square(5):
9
    print(num)

结果:

1
before yield
2
0
3
after yield
4
before yield
5
1
6
after yield
7
before yield
8
4
9
after yield
10
before yield
11
9
12
after yield
13
before yield
14
16
15
after yield

说明:

  • 可以看到,使用生成器函数同样实现了与普通函数同样的功能。
  • 它们有如下区别:生成器代码更简洁。 对比两者的代码,除了用于打印提示信息的代码之外,生成器的代码量更少,结构更为简洁。生成器内存占用极少。生成器并没有创建列表,更不会因为自然数的变大而消耗大量的内存; 普通函数则面临严重的内存问题。运行方式不同。 普通函数是顺序执行的,直到执行完最后一行语句或者遇到return语句就返回;而生成器函数则是遇到yield语句返回,再次执行时,从上次离开的地方继续执行。
参考:
- 《物联网Python开发实战》
免费知识星球:一番码客-积累交流
微信公众号:一番码客
微信:Efon-fighting
网站:http://www.efonmark.com

蜀ICP备19039940号

总访问量为