文章目录
- Python高级特性
- 切片
- 迭代
- 判断是否可迭代
- 如何实现下标循环
- 列表生成式
- ⭐️生成器
- 什么是生成器?
- 为什么要引入生成器?
- 如何使用生成器?
- generator和函数的区别
- 迭代器
- Iterable与Iterator
Python高级特性
python中提供了切片、迭代、列表生成式、生成器、迭代器等诸多高级特性,多多使用高级特性,可以使得我们的代码更加简单,Python的特点就是代码简单。1行代码能实现的功能,决不写5行代码。
切片
对于取指定索引范围的操作,Python提供了切片(Slice)操作符(用循环十分繁琐)。切片操作对于List、tuple、字符串都同样适用,切片返回的结果仍然是原类型。举几个例子:
操作 | 写法 | 说明 |
取前3个元素 |
| 第1个索引是0可以省略 |
取第i~j个元素 |
| 取切片的范围是到n-1 |
取倒数第1个元素 |
| -1表示最后一个元素 |
取后10个数 |
| 负号表示倒数, |
前10个数,每两个取一个 |
| |
所有数,每五个取一个 |
|
迭代
给定一个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