Python笔记(五)-- 高级特性

切片,全局变量,生成器

切片(slice)

切片(slice)用于取一个list或tuple的部分元素,比如,一个list如下:

1
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']

L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。如果第一个索引是0,还可以省略:

1
2
>>> L[:3]
['Michael', 'Sarah', 'Tracy']

类似的,既然Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片。

1
2
3
4
>>> L[-2:]
['Bob', 'Jack']
>>> L[-2:-1]
['Bob']

所有数,每5个取一个:

1
2
3
>>> L = list(range(100))
>>> L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

甚至什么都不写,只写[:]就可以原样复制一个list(这可以用来解决默认参数重复赋值问题):

1
2
>>> L[:]
[0, 1, 2, 3, ..., 99]

tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:

1
2
>>> (0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)

字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:

1
2
3
4
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'

全局变量和局部变量

1
2
3
4
5
6
7
8
A = 10
def fun():
global A
A = 20
print(A) # 10
fun()
print(A) # 20

generator

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1
2
3
4
5
6
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

1
2
3
4
5
6
7
8
9
>>> next(g)
0
>>> next(g)
1
# 执行到最后一个元素的时候
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

通常,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

1
2
3
4
5
6
7
>>> g = (x * x for x in range(3))
>>> for n in g:
... print(n)
...
0
1
4

yeild 定义generator

Fibonacci sequence(斐波那契数列)

1
2
3
4
5
6
7
8
9
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(a)
a, b = b, a+b
n = n+1
return 'done'
fib(10)

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

1
2
3
4
5
6
7
8
print("====================")
def fibGenerator(max):
n, a, b = 0, 0, 1
while n< max:
yield a
a, b =b, a+b
n += 1
return 'done'

调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
f = fibGenerator(10)
print(next(f))
print(next(f))
print(next(f))
print('=====================')
for x in f:
print(x)
# 输出结果如下
0
1
1
=====================
2
3
5
8
13
21
34

同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

1
2
3
4
5
6
7
print(fibGenerator(10))
for x in fibGenerator(10):
print(x)
print("====================")
L = [x for x in fibGenerator(10)]
print(L)

用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
while True:
try:
x = next(f)
print('f: ', x)
except StopIteration as e:
print('Generator return value: ', e.value)
break
# 输出结果如下
f: 0
f: 1
f: 1
f: 2
f: 3
f: 5
f: 8
f: 13
f: 21
f: 34
Generator return value: done

练习–杨辉三角

杨辉三角定义如下:

1
2
3
4
5
6
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1

把每一行看做一个list,试写一个generator,不断输出下一行的list:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 以下是我写的渣渣版
def triangles(max):
n = 1
if n == 1:
L = [1]
# print(L)
yield L
n = n + 1
L = [1,1]
# print(L)
yield L
while n < max:
L = list([*L, 1])
L[0] = 1
L[n-1] = 1
l = L[:]
for i in range(1, n):
L[i] = l[i-1] + l[i]
n = n + 1
yield L
# print(L)
t = triangles(10)
for i in t:
print(i)
# 输出结果如下
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
# 下面是网友给出的简洁版,再次被秒成渣
def trian(max):
L=[1]
n = 0
while n < max:
yield L
L = [1] + [ L[x-1] + L[x] for x in range(1,len(L)) ] + [1]
n += 1
t = trian(10)
for i in t:
print(i)

参考资料

  1. 廖雪峰python教程