Python自学记录——列表生成式、生成器、迭代器

前段时间因工作问题和家中的电脑无法开机,学习Python的计划搁置了一段时间。借此感觉到以前学习的速度过慢,为了达成自己制定的学习目标,决定延长每次Python的学习时间,增加记录内容。下面正题开始:

列表生成式

即快速生成 list 的写法,它内置于Python中,简单又强大。

举个例子,生成一个数字1到10的列表,写法如下:

L = range(1,11)
L
[1,2,3,4,5,6,7,8,9,10]

若想得到元素乘积,则列表生成式写法如下:

[x * x for x in L]
[1,4,9,16,25,36,49,64,81,100]

不用列表生成式的写法如下:

L1 = []
for x in L :
    L1.append(x * x)
L1
[1,4,9,16,25,36,49,64,81,100]

有此可见,列表生成式简化了很多代码,使用起来也十分方便。

继续往下走,在循环中加一个条件,使列表只得到偶数的乘积,则写法如下:

[x * x for x in L if x % 2 == 0]
[4,16,36,64,100]

列表生成式的基本写法如上所示,它还可以内置多个循环,形成全排列,示例如下:

[ x + y for x in 'ABC' for y in 'XYZ']
['AX','AY','AZ','BX','BY','BZ','CX','CY','CZ']

一般情况下,只使用两层循环,三层及三层以上的循环就很少使用了。列表生成式还可拼接字典dict,示例如下:

D = {'a':'1','b':'2','c':'3'}
[k+'='+v for k,v in D.items()] 
['a=1','c=3','b=2']

由于dict内是无序的,所以输入的结果顺序对应会有些差异,此问题可以忽略不计。

测试题:

L3 = ['Apple','Banana',18,'Orange',None]

上述list,要求把大写字母变为小写字母,错误写法如下:

[x.lower() for x in L3]
Traceback (most recent call last):
  File "<stdin>",line 1,in <module>
AttributeError: 'int' object has no attribute 'lower'

报错结果,大致说 int 类型的元素不支持 lower 方法,所以我们正确的写法需要加判断来筛选下 ,正确示例如下:

[x.lower() if isinstance(x,str) else x for x in L3]
['apple','banana',18,'orange',None]

由此,可以总结出列表表达式的写法如下:

[执行方法(得到值) 附加条件  循环  简单判断条件(可写可不写)]

我认为循环语句的执行过程如下:

1.循环遍历出第一个值;

2.若有简单判断条件进行判断,若无,跳过这步,进行下一步;

3.若有附加条件,先判断附加条件,此时写法上有一点需要注意,if成功的结果会调用执行方法(得到值)的方法对遍历得到的值进行计算,if 后不需要写值,else 后需要些对应的执行方法。若只判断一次,则将判断条件写在 简单判断条件 的位置上,附加条件不写,有else的情况 才把判断都移到前面来,示例如下:

[x.lower() if isinstance(x,str) else x for x in L3]
此为需要else的写法
[x.lower() for x in L3 if isinstance(x,str)]
此为不需要else的写法

4.把值存放到 list 中

4.循环遍历下一个值,重复执行步骤2,3,4。

列表生成式就到这里,下面是生成器。

生成器

在Python中,有一种边循环边计算的机制,称为 生成器generator。由于它是边循环边计算的,所以使用时会节省很多空间。

它的使用方法也很简单,与 列表生成器 类似,将 [ ] 替换为 ( ) 即可。与list对比的示例如下:

[x * x for x in range(10)]
[0,1,4,9,16,25,36,49,64,81]
(x * x for x in range(10))
<generator object <genexpr> at 0x0000000002116A20>

list 创建完成后是一个可见的列表,而generator创建出来的对象不能直接看到它包含的信息,若想打印看到,需要使用next()方法,具体示例如下:

g = (x * x for x in range(10))
next(g)
0
next(g)
1
next(g)
4
next(g)
9
……
next(g)
81
next(g)
Traceback (most recent call last):
  File "<stdin>",line 1, in <module>
StopIteration

由于篇幅有限,循环过程省略了一部分,当next()方法运行到数据之外的时候,会报错,告诉用户没有更多元素了。

若让我们打印generator运行的数据,只用next()方法来写,有些尴尬。。正确的方法是用for来遍历打印,generator也是一个可迭代对象,示例如下:

g1 = (x * x for x in range(10))
for n in g1:
    print(n)

0
1
4
9
……
81

此处联系时需注意,不要使用前面的 g 对象,因为 g 对象中generator 已经全遍历出来了,for循环时没有能遍历出的值,这不会报错。所以,新建一个 generator 对象,使用for循环遍历它。结果如上面示例所示。

数学上有个著名的数列,称为 斐波拉契数列,它是 除第一个和第二个数外,任意一个数都可由前两个数相加得到,使用列表生成式无法得到这个数列,但欧阳函数打印出来很容易,示例如下:

def tfb(num):
    n ,a ,b = 0 ,0 ,1
    while n < num:
        print(b)
        a ,b = b ,a + b
        n = n + 1
    return 'end'

tfb(5)

1
1
2
3
5
'end'

上述代码中,n,a,b = 0,0,1,是创建n,a,b三个对象,并给他们赋值为0,0,1,即n=0,a=0,b=1。同理,a,b=b,a+b为a=b,b=a+b。

这个方法和生成器的运行逻辑十分相似,只需要一点改动便可将其变为generator,即 将 print(b) 替换为 yield b。这是定义generator的另一种写法,如果一个函数中包含 yield 关键字,那它就不是一个普通的函数,而是一个 generator。示例如下:

def tfl(num):
    n ,a ,b = 0 ,0 ,1
    while n < num:
        yield b
        a ,b = b ,a + b
        n = n + 1
    return 'end'

tfl(5)

<generator object tfl at 0x0000000002116C00>

for x in ftl(5):
    print(x)

1
1
2
3
5

generator和一般的函数执行流程不太相同。一般函数是执行结束后返回需要的指定值;generator 是遇到 yield 就返回指定值,下次执行接着上次 的 yield 执行,执行到首次出现 yield 再返回指定值。具体示例如下:

def g1(num):
    yield num+1
    yield num+2
    yield num+3
    return 100

g = g1(1)

next(g)
2
next(g)
3
next(g)
4
next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上述示例中会发现,return的值无法被取到,若想要得到return 的值,需要从异常中捕获StopIteration,从StopIteration.value中取值,具体示例如下:

gg = g1(1)
while True:
    try:
        x = next(gg)
        print('g:',x)
    except StopIteration as e:
        print('return value:',e.value)
        break

g: 2
g: 3
g: 4
return value: end

以杨辉三角为联系,结合百度和自身想要的需求,实现根据输入数字,返回指定行数的list(默认为5):

def triangles(num = 5):
    L = [1]
    n = 0
    while n < num:
        yield L
        L.append(0)
        L = [L[i-1] + L[i] for i in range(len(L))]
        n = n+1

for x in triangles():
    print(x)

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]

迭代器

在此之前,要重申一个概念,叫 可迭代对象Iterable 。

可迭代对象Iterable是指可以通过 for循环 来遍历出值的对象,例如 list、str、dict等。

而可迭代器Iterator是指可以通过 next() 方法来得到值 的对象,如上述 generator 等。这类的对象都是惰性的,因为我们只有通过不断next() 来计算出下一数据。

借他人总结之言,总结下:

1.凡是可作用于for循环的对象都是Iterable类型;

2.凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

3.集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

4.Python的for循环本质上就是通过不断调用next()函数实现的

本篇就到这里,教材网址:https://www.liaoxuefeng.com, 继续学习~~

猜你喜欢

转载自blog.csdn.net/qq_37647296/article/details/81773164