列表生成式&生成器&迭代器
一、列表生成式
Python内置的一种极其强大的生成列表 list 的表达式。返回结果必须是列表。
1.1、基本语法
[ 变量表达式 for 变量 in 表达式 ]
1.2、示例
a = [x ** 2 for x in range(1, 10)]
b = [x * x for x in range(1, 11) if x % 2 == 0]
c = [m + n for m in 'ABC' for n in '123']
d = {'Java': "99", 'C': "99", 'C++': "99"}
L = [k + '=' + v for k, v in d.items()]
print(a)
print(b)
print(c)
print(L)
1.3、说明
这样的写法就叫做列表生成式。通过列表生成式,可以直接创建一个列表。
二、生成器(generator)
2.1、背景
前面我们学习到了,通过列表生成式,我们可以直接创建一个列表。但是,受到内存的限制,列表容量是有限的,当列表元素很大的时候,会很浪费内存空间。(如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了)。为了解决这种问题,在Python中,提供了生成器(generator),生成列表的前几个元素,当需要用到后面的时候,在计算生成。从而节省内存空间。
2.2、定义
在Python中,这种一边循环一边计算后面元素的机制,称为生成器:generator。
2.3、使用
2.3.1、如何创建生成器
a、使用()类似列表生成式的生成器
使用方法
把列表生成式的中括号 [] 修改为圆括号即可 ()
代码示例
a = (x ** 2 for x in range(1, 10))
b = (x * x for x in range(1, 11) if x % 2 == 0)
c = (m + n for m in 'ABC' for n in '123')
d = {'Java': "99", 'C': "99", 'C++': "99"}
L = (k + '=' + v for k, v in d.items())
print(a)
print(b)
print(c)
print(L)
输出结果:
<generator object <genexpr> at 0x1052ec2b0>
<generator object <genexpr> at 0x1052ec468>
<generator object <genexpr> at 0x1052ec4c0>
<generator object <genexpr> at 0x1052ec518>
b、使用包含 yield 关键字的函数生成器
背景
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, …
这个就无法使用类似列表生成式定义。就要使用到函数生成器
实现100以内的斐波那契数代码:
def fib(max):
a,b = 0,1
n = 0 # 斐波那契数
while n < max:
n = a + b
a = b # 把b的旧值给到a
b = n # 新的b = a + b(旧b的值)
print(n)
fib(100)
使用方法
在上述代码中,添加 yield 关键字,我们就可以将该函数变为函数生成器。
代码示例
def fib(max):
a,b = 0,1
n = 0 # 斐波那契数
while n < max:
n = a + b
a = b # 把b的旧值给到a
b = n # 新的b = a + b(旧b的值)
#print(n)
yield n # 程序走到这,就会暂停下来,返回n到函数外面,直到被next方法调用时唤醒
f = fib(100) # 注意这句调用时,函数并不会执行,只有下一次调用next时,函数才会真正执行
print(f)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
输出结果:
1
2
3
5
说明
这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句暂停并返回数据到函数外,再次被next()调用时从上次返回的yield语句处继续执行。
2.3.2、如何访问生成器中的元素
a、使用 next 调用元素
作用:
- next 方法会一个个的返回元素值,调用一次,返回一次下一个位置的元素。
- 该方法在没有元素可以调用的时候,会返回 StopIteration 的错误
代码示例
>>> g = (x * x for x in range(10))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "", line 1, in
StopIteration
b、使用for循环遍历
作用
- 上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代(遍历)对象
- 通过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
三、迭代器
3.1、可迭代对象(Iterable)
3.1.1、什么是可迭代对象
我们已经知道,可以直接作用于for循环的数据类型有以下几种:
- 一类是集合数据类型,如list、tuple、dict、set、str等;
- 一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable,可迭代的意思就是可遍历、可循环。
3.1.2、如何判断可迭代对象
可以使用isinstance()判断一个对象是否是Iterable对象:
代码示例
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
3.2、迭代器(Iterator)
3.2.1、说明
- 迭代是Python最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法:iter() 和 next()。
3.2.2、创建迭代器
a、创建普通迭代器
字符串,列表或元组对象都可用于创建迭代器:
>>> list=[1,2,3,4]
>>> it = iter(list) # 创建迭代器对象
>>> print (next(it)) # 输出迭代器的下一个元素
1
>>> print (next(it))
2
>>>
b、创建类迭代器
说明
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()
与 __next__()
。
__iter__()
方法返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__()
方法并通过 StopIteration 异常标识迭代的完成。__next__()
方法(Python 2 里是 next())会返回下一个迭代器对象。
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
代码示例
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
输出结果:
1
2
3
4
5
补充
StopIteration异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__()
方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
代码示例:
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
3.2.3、迭代器元素访问
使用 next() 函数
#!/usr/bin/python3
import sys # 引入 sys 模块
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
while True:
try:
print (next(it))
except StopIteration:
sys.exit()
使用常规for语句进行遍历
#!/usr/bin/python3
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
for x in it:
print (x, end=" ")
3.2.4、总结
- 凡是可作用于for循环的对象都是Iterable类型;
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
- 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。