[TOC]
Python高级特性
python中提供了切片、迭代、列表生成式、生成器、迭代器等诸多高级特性,多多使用高级特性,可以使得我们的代码更加简单,Python的特点就是代码简单。1行代码能实现的功能,决不写5行代码。
切片
对于取指定索引范围的操作,Python提供了切片(Slice)操作符(用循环十分繁琐)。切片操作对于List、tuple、字符串都同样适用,切片返回的结果仍然是原类型。举几个例子:
| 操作 | 写法 | 说明 |
|---|---|---|
| 取前3个元素 | L[0:3]、L[:3] | 第1个索引是0可以省略 |
| 取第i~j个元素 | L[i:j+1] | 取切片的范围是到n-1 |
| 取倒数第1个元素 | L[-1] | -1表示最后一个元素 |
| 取后10个数 | L[-10:] | 负号表示倒数,-10自然就表示倒数第10个元素,最后1个索引不写默认为n |
| 前10个数,每两个取一个 | L[:10:2] | |
| 所有数,每五个取一个 | L[::5] |
迭代
给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。list、tuple、str、dict都是可迭代对象。
判断是否可迭代
通过collections.abc模块的Iterable类型可以判断一个对象是否是可迭代对象:
>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
注:凡是可作用于for循环的对象都是Iterable类型;
如何实现下标循环
Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
上面的for循环里,同时引用了两个变量,在Python里是很常见的,比如下面的代码:
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9
列表生成式
列表生成式(List Comprehensions),又称列表解析式,是Python内置的非常简单却强大的可以用来创建list的生成式。==运用列表生成式,可以写出非常简洁的代码==。
注:其实numpy中的一些内置函数功能更为强大且更加常用。
-
例子1,要生成list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:>>> list(range(1, 11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -
例子2,生成仅偶数的平方和List: 把要生成的元素x*x放到前面,后面可以加if
>>> [x*x for x in range(1,11) if x % 2 == 0] [4, 16, 36, 64, 100] -
还可以加上判断条件
if-else,但要加在for的前面,for后面的if是过滤条件,不能带else。 例子3,把L1的字符全部变为小写,剔除非字符串。>>> L1 = ['Hello', 'World', 18, 'Apple', None] >>> L2 = [str.lower(x) for x in L1 if isinstance(x, str)] ['hello', 'world', 'apple']
⭐️生成器
生成器(generator)是Python中非常有特点的一个特性,其他主流语言中往往没有这个特性。
什么是生成器?
如果列表元素可以按照某种算法推算出来,我们可以在循环的过程中不断推算出后续的元素。这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
为什么要引入生成器?
说生成器之前,先说说列表解析式,他的优点很多,比如运行速度快、编写简单,但是有一点我们不要忘了,他是一次性生成整个列表。如果整个列表非常大,这对内存也同样会造成很大压力,想要实现内存的节约,可以将列表解析式转换为生成器表达式。
如何使用生成器?
-
创建生成器 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的
[]改成(),就创建了一个generator。创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。 -
遍历生成器
-
generator保存的是算法,每次调用
next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。 -
我们通常都是通过都是
for循环来迭代它,并且不需要关心StopIteration的错误。>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) ... 0 1 4 9 16 25 36 49 64 81 -
如果推算的算法比较复杂,用类似列表生成式的
for循环无法实现的时候,还可以用函数来实现。==如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个生成器函数(generator function)==:# 输出斐波那契数列 def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done'
-
generator和函数的区别
generator和函数的执行流程不一样。
函数是顺序执行,每次调用从头执行,遇到return语句或者最后一行函数语句就返回。
而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。直到没有yield可以执行了就报错。
杨辉三角代码:
# -*- coding: utf-8 -*-
def triangles():
L = [1]
while True:
yield L[:]
L.append(0)
L = [L[i-1]+L[i] for i in range(len(L))]
n = 0
results = []
for t in triangles():
results.append(t)
n = n + 1
if n == 10:
break
for t in results:
print(t)
输出:
[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]
迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
例如生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
Iterable与Iterator
可以直接作用于for循环的对象统称为可迭代对象:Iterable,如list、tuple、dict、set、str等。**生成器都是Iterator对象,但Iterable却不一定是Iterator。**例如list、dict、str虽然是Iterable,却不是Iterator。
Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以**Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。**Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
凡是可作用于for循环的对象都是Iterable类型;凡是可作用于next()函数的对象都是Iterator类型。
集合数据类型如list、dict、str等Iterable对象可以通过iter()函数获得一个Iterator对象。
迭代器是Python内置的重要特性,Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break