一. 解析式 / 推导式 Comprehension
解析式,也叫推导式(comprehensions),它是 python 的一种独有特性。解析式是可以从一个数据序列构建另一个新的数据序列。
解析式包括:列表解析式、字典解析式、集合解析式。
1. 列表解析式 list comprehension
先来看个例子: 有个列表 lst =list(range(10)),若将 lst 每个元素自增 1后求平方,然后生成一个新列表。新列表 lst_new ?
lst = list(range(10))
lst_new = []
for el in lst:
lst_new.append((el + 1) ** 2)
而使用列表解析式,这样来写:
lst = list(range(10))
lst_new = [(el + 1) ** 2 for el in lst]
那列表解析式长啥样呢?
它的基本语法格式:
[表达式 for 元素 in 可迭代对象 if 条件]
[表达式 for 元素 in 可迭代对象1 for 元素 in 可迭代对象2]
...
说明:
- 列表解析式,既叫列表,那就是用 [ ] 括起来的;
- [] 内部用 for 循环,if 条件是可选的;
- 返回一个新列表
列表解析式 其实是一种语法糖
- 编译器对它作了优化,提高了效率
- 简化了代码,增强了可读性
【实例 1】10 以内的偶数
even = [i for i in range(10) if i%2==0]
# 用append 函数就是
even = []
for x in range(10):
if x % 2 == 0:
even.append(x)
想一想 ↓↓
one_lst = [print(i) for i in range(5)] # [None, None, None, None, None]
one_lst = [i for i in range(5)] # [0, 1, 2, 3, 4]
练习一下 ↓
[i for i in range(20) if i%2==0 and i%3==0]
[i for i in range(20) if i%2==0 if i%3==0]
再来 ↓↓
[(x, y) for x in 'abc' for y in range(1, 3)]
[[x, y] for x in 'abc' for y in range(1, 3)]
[{x: y} for x in 'abc' for y in range(1, 3)]
# 列表解析式里有2个 for 时,等价于
lst = []
for i in iterable_1:
for j in iterable_2:
lst.append(expr)
2. 集合解析式 set comprehension
集合解析式与列表解析式非常类似,把 [ ] 改成 { } 即可。基本格式:
{表达式 for 元素 in 可迭代对象 if 条件}
立即返回一个集合
【实例 2】10以内偶数的集合
{x for x in range(10) if x%2==0}
3. 字典解析式 dict comprehension
字典解析式是列表解析式的延续,语法也差不多,不同的只是生成的是字典而已。
基本格式:
{表达式 for 元素 in 可迭代对象 if 条件}
与集合解析式不同的是,表达式是 key:value 字典形式的,立即返回一个字典。
【实例 3】字典解析式的应用
{str(x):y for x in range(3) for y in range(4)} # {'0': 3, '1': 3, '2': 3}
总结:
- 解析式有很多优势,在不复杂的循环中建议多用;
- 但是,如解析式太复杂,难以读懂,建议使用 for 循环;
- 生成器和迭代器是不同的对象,但都是可迭代对象;
**
二. 生成器 Generator
生成器 generator,在 python 中它是一边循环一边计算的一种机制,它保存的不是目标数据,而是一套生成数据的算法,且不会立即执行,而是在调用它的时候才会计算并返回。
为什么要有生成器呢? 为了节省内存空间。 列表的数据是完整的放在内存中的,在数据规模非常大的时候,是很耗内存的,而如果列表中靠后的数据不会用到的话,那可能有很大的内存就白白浪费了。为了解决这个现象就有了生成器了。生成器不用创建完整的list数据,调用一个计算并返回一个,从而节省了空间。
生成器指的是生成器对象,创建生成器有两种方法:“元组”解析式(生成器表达式)、yield 关键字。
1. 生成器之“元组”解析式 *
解析式形式的基本语法格式:
(表达式 for 元素 in 可迭代对象 if 条件)
说明:把列表解析式的 [] 改成小括号 () 就是一个生成器;
和列表解析式的区别:
- 生成器表达式,是按需计算(或称惰性求值、延迟计算),需要的时候才计算值
生成器(表达式) | 列表解析式 |
---|---|
g = (“{:03}”.format(i) for i in range(1,11)) next(g) for x in g: print(‘Loop 1: {}’.format(x)) for x in g: print(‘Loop 2: {}’.format(x)) |
g = [“{:03}”.format(i) for i in range(1,11)] print(g) for x in g: print(‘Loop 1: {}’.format(x)) for x in g: print(‘Loop 2: {}’.format(x)) |
返回一个生成器对象(可迭代的对象) | 返回一个新列表 |
按需计算 | 立即计算并返回完整值 |
耗时短 | 耗时长 |
返回迭代器,可迭代 | 返回可迭代对象列表,可迭代 |
遍历完一遍后不能回头再次遍历 | 遍历完一遍后可以再次遍历 |
2. 生成器之 yield
生成器也可以使用 yield 关键字得到一个生成器函数,调用这个函数就会得到生成器对象。
yield 生成器函数
函数体中包含 yield 语句的函数,返回生成器对象。一个简单的生成器函数,先看看它是怎么运作滴:
def gener():
yield 1
yield 2
yield 3
g = gener()
说明:
- 调用生成器函数 gener() 得到生成器 g;
- 通过next()函数执行生成器,当执行到 yield 语句时,返回 yield 表达式的值后会暂停执行(返回 1);
- 当再次next()执行时,从 yield 1 下面的一句开始执行,遇到 yield 语句就返回表达式的值后暂停,这样往复执行
注意 要想使生成器函数执行,有 2 种方式:
- 通过生成器调用 next() 函数(或next());
- 通过 for 循环遍历生成器(for 底层会不断的调用 next() 函数);
实际中多数这样使用生成器:
def intNum():
for i in range(5):
yield i
print("再次调用从这里继续执行")
num = intNum()
说明:上面代码中 num 生成器(对象),用 next(num) 可看到 yield 返回的结果。
小结 生成器函数:
- 生成器函数等价于生成器表达式,简单的语句或逻辑使用生成器表达式,而过于复杂的就使用生成器函数。
- 和普通函数不同,生成器函数的返回值用的是 yield 关键字,而不是 return。
- 调用生成器函数时生成生成器对象,而函数体是不会立即执行的。
- 一般的函数被调用后会立即执行完返回结果,而生成器函数通过 next() 函数可被多次执行。
- 没有多余的 yield 语句能被执行时,如果继续调用 next() 会抛出异常。
生成器函数的应用
【实例 4】斐波那契数列,应用在递归问题中
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
【实例 5】计数器
def inc():
def counter():
i = 0
while True:
i += 1
yield i
c = counter()
return lambda : next(c)
foo = inc()
print(foo())
print(foo())
yield from
**
基本语法格式:
yield from iterable
for item in iterable:
yield item
yield from 它是由for循环而来,是它的语法糖。
yield from range(1000)
for x in range(1000):
yield x
**
三. 迭代器
- 可迭代对象 iterable
可迭代对象 可理解成可以被 for 循环迭代的对象就是可迭代对象。其实本质上说,一个对象中有 iter() 方法,那这个对象就是可迭代对象。
- 迭代 是访问集合元素(理解为容器类型)的一种方式。
- 以作用于 for 循环的数据类型,可迭代对象:
- 集合(容器)类型:如 str、list、tuple、dict、set 等;
- generator 生成器
判断可迭代**?**
使用 isinstance() 判断一个对象是否是 iterable 对象
from collections import Iterable
isinstance(100, Iterable) # False
isinstance('abc', Iterable) # True
isinstance([1, 2, 3], Iterable) # True
isinstance((4, 5, 6), Iterable) # True
注意,可迭代对象可以迭代,但未必有序,未必可索引。
2. 迭代器 iterator
迭代器 是一个可以记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。本质上,一个可被 next() 函数调用并不断返回下一个值的对象就是 iterator。
判断迭代器?** **
使用 isinstance() 判断一个对象是否是 iterator
from collections import Iterator
isinstance(100, Iterator) # False
isinstance('abc', Iterator) # False
isinstance([1, 2, 3], Iterator) # False
isinstance((4, 5, 6), Iterator) # False
isinstance((i for i in range(3)), Iterator) # True
iter() 方法 通过 iter() 方法,可以把一个可迭代对象封装成迭代器
from collections import Iterator
isinstance(iter('abc'), Iterator) # True
注意:str、list、tuple、dict、set等是 iterable,但不是 iterator
3. 可迭代对象、迭代器、生成器的关系
从上图可以看出:
- 可迭代对象包含迭代器;
- 一个对象有 iter() 方法,就是可迭代对象;
- 一个可迭代对象,可被 next() 方法调用,就是迭代器;