Python高级特性

迭代

Python中的循环遍历称之为迭代(Iteration),我们可以通过collections模块里面的Iterable进行判断一个对象是否可以进行迭代。

1
2
from collections import Iterable
isinstance(object, Iterable)

如果需要对list实现像Java一样的下标循环,可以使用enumerate()函数将一个list变成索引-元素对

1
for i, value in enumerate(['A', 'B', 'C'])

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    >>>
0 A
1 B
2 C
```

### 列表生成式

列表生成器(list comprehension)可以方便的使用一句代码实现循环。
**循环实现:**
```python
L = []
for x in range(1, 11):
L.append(x*x)

列表生成器实现:

1
[x*x for x in range(1, 11)]

列表生成器的for循环后面还可加上if条件判断,比如筛选出仅偶数的平方:

1
[x*x for x in range(1, 11) if x % 2 == 0]

还可以使用双层循环实现全排列:(三层和三层以上的就很少用到了)

1
[m + n for m in 'ABC' for n in 'XYZ']

输出结果:

1
2
>>>
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

生成器

通过列表生成式可以方便的实现一些循环操作,但是如果数据一旦很大的话,列表生成式会占用很大存储空间很容易导致内存溢出,如果仅仅是需要使用数据的前面几个数据的话,那么后面的绝大多数数据占用的空间会被白白浪费掉。因此,Python中提供一边循环一边计算的机制,这个机制就叫生成器(generator)
创建一个generator非常简单,只用将列表生成式的[]换成()即可创建一个generator,列表生成式和generator的区别就是,列表生成式很容易的将整个的数据全部打印出来,但是generator的数据并不能直接打印出来,它需要通过next()函数来进行获取generator的下一个返回值。这就意味着generator保存的是算法,每次调用next()函数会进行一次计算,直到计算到最后一个元素时,再次调用计算时就会抛出StopIteration的异常,为了防止抛出``StopIteration的异常,正确的方式是使用for来代替next()`函数进行generator的迭代。

1
2
3
g = (x*x for x in range(1, 11))
for n in g:
print(n)

generator 能够很好的解决数据庞大for循环导致的内存空间占用的问题,但是如果推算的算法比较复杂的时候,用类似于列表生成式的for循环无法实现的时候,这个时候就需要使用函数来实现。比如要实现斐波那契数列(Fabonacci:数列除了第一个数和第二个数外,任意一个数都可以由前两数的和相加得到:1,2,3,5,8 ...)这个数列就无法使用简单的列表生成式的generator实现出来,这时候就需要使用到函数。

1
2
3
4
5
6
7
def fibnacci(count):
n, a, b = 0, 0, 1
while n < count:
print(b, end= ',')
a, b = b, a + b
n = n + 1
return 'The end'

输出结果:

1
2
>>> fibnacci(5)
1,1,2,3,5

上面的函数和generator非常相像,要把函数变成一个generator只需要将print(b)这一句换成yield b,这里定义的就是generator的另一种方式,如果函数定义中包含yield关键字,此时的函数就不仅仅是一个函数了,而是一个generator,需要注意的是generator和函数的执行方式有所不同,函数是顺序执行遇到return或者执行到最后一句的时候就会返回,而generator函数,只有在在每次的调用next()函数时执行,遇到yield语句就返回,再次执行的时候从上次返回的yield语句出继续执行

1
2
3
4
5
6
7
8
9
10
11
def fibnacci(count):
n, a, b = 0, 0, 1
while n < count:
yield b
a, b = b, a + b
n = n + 1
return 'The end'

# 使用for循环对generator函数进行迭代输出
for value in fibnacci(5):
print(value)

同样generator函数基本不会使用next()函数来进行获取下个返回值,而是直接使用for循环来迭代,但是for循环调用generator时,你会发现无法获取return语句的返回值,如果想要拿到return返回值就必须捕获StopIteration异常,返回值包含在StopIterationvalue里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def fibnacci(count):
n, a, b = 0, 0, 1
while n < count:
yield b
a, b = b, a + b
n = n + 1
return 'The end'

g = fibnacci(5)
while True:
try:
value = next(g)
print(value)
except StopIteration as e:
print(e.value)
break

输出结果:

1
2
3
4
5
6
7
>>> fibnacci(5)
1
1
2
3
5
The end

迭代器

在Python中可以直接for循环的数据类型有以下几种:
一种是集合数据类型:list tuple dict set str
一种是generator: 包括生成器和带yield关键字的生成器函数
这些可以直接ffor循环的对象统称为可迭代对象:Iterable。而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。在Python中Iterator对象表示的是一个数据流,可以将这个数据流看成是一个有序序列,但是我们却不能提前知道这个序列的长度,只能不断地使用next()函数实现下一个数据的计算,所以Iterator是一个惰性计算。生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator。把listdictstrIterable变成Iterator可以使用iter()函数。

小提示:
Python的for循环本质就是通过不断调用next()函数实现的

1
2
3
test = [1, 2, 3, 4, 5]
for x in test:
pass

实际等价于:

1
2
3
4
5
6
Iterator_object = iter(test)
while True:
try:
x = next(Iterator_object)
except StopIteration:
break