列表生成式
#生成[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#直接创建
arr = list(range(0, 10))
print(arr)
#列表生成式
arr = [x for x in range(0, 10)]
print(arr)
上面两段代码效果相同,都是生成0~9的list,第二段代码使用的是列表生成式。
其中第二个x
表示for..in
迭代的元素,第一个x
表示添加到list中的元素;第一个x
还可以替换成其他的,例如表达式
arr = [0 for x in range(0, 10)]
print(arr)
#输出:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
arr = [x*2 for x in range(0, 10)]
print(arr)
#输出:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
在for..in
后面还能加上if
条件判断。抱着怀疑的态度,试了试在直接使用for..in
进行迭代的情况下添加if
判断,结果报错了。
arr = [x * x for x in range(0, 10) if x % 2 == 0]
print(arr)
#输出:[0, 4, 16, 36, 64]
还有一个比较强大的地方,列表生成式支持for..in
的叠加,也就是循环的嵌套
arr = [m + n for m in '123'for n in 'abc']
print(arr)
#输出:['1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']
for..in
所支持的迭代类型,在列表生成式中都是可以使用的,例如字符串,dirt,dirt.values(),dirt.items()等,都是可以使用的。而在列表生成式的第一个x
的位置,出了表达式,还支持第二个x
类型所支持的函数。
生成器(generator)
列表生成式是一次直接创建了一个完整的list
,而生成器则不必创建一个完整的list
,可以一边循环一边创建list
中的元素,这样跟更加节省内存。
#生成式中的[]变成()
g = (x for x in range(10))
这样就创建了一个generator
,并不是list
,可以通过next(g)
函数获取生成的元素,next
会获取下一个生成的元素。而generator
也是支持迭代的,这样才更加合理,不需要无限的调用next(g)
,并且不需要关心StopIteration
的错误。当next(g)
不能生成元素了会报StopIteration
错误。
g = (x for x in range(10))
for n in g:
print(n)
在较复杂的情况下,无法用迭代完成时,generator
可以用函数来实现。比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
上面的函数打印了斐波那契数列的前max
个数,这里只需要把print(b)
修改成yield(b)
,fib
函数就变成了一个generator
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield(b)
a, b = b, a + b
n = n + 1
return 'done'
直接打印fib(4)
时,已经不是按照之前的规则输出数列,结束时输出done,而是这样一串字符
<generator object fib at 0x000001E7F04B8258>
这是因为generator
和函数的执行流程不同,函数是顺序执行遇到return
或者执行完成就结束了;而generator
则是在每次条用next
时执行,遇到yield
语句返回,再次执行next
时从上次的yield
继续执行。举个例子:
def odd():
print('step 1')
yield (1)
print('step 2')
yield (2)
print('step 3')
yield (3)
o = odd()
print(next(o))
print(next(o))
print(next(o))
输出结果:
step 1
1
step 2
2
step 3
3
从输出结果可以看得出来,每调用一次next
都会停在yield
的地方,下一次执行next
有会接着从这里开始。执行3次yield
后,已经没有yield
可以执行了,所以,第4次调用next(o)
就报错。
同时这里还会发现一个问题,当使用
yield
将函数变成generator
函数是无法获取return
的值。如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中。
迭代器
之前提高过迭代的概念,也就是for..in
,支持的数据类型有list
、tuple
、dict
、set
、str
等;generator
和generator
函数也是支持的,这些都是Iterable
对象。在迭代中有提到isinstance()
函数可以判断一个对象是否是Iterable
对象。
其中generator
不仅可以使用for
循环并且可以使用next()
函数并不断返回下一个值,这样的对象被称为迭代器(Iterator)。
同样可以使用isinstance()
函数判断一个对象是否是Iterator
对象
from collections import Iterator
print(isinstance([], Iterator))
print(isinstance({}, Iterator))
print(isinstance('', Iterator))
以上上种类型输出的结果都是False
这里有一点需要注意,generator
是Iterator
,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。使用iter()
函数可以将list
、dict
、str
等Iterable
变成Iterator
。
for
循环的本质就是通过不断条用next()
函数实现的。
网友评论