Python列表生成式
列表推导式的一般语法
[express for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
....
for itemn in iterablen if conditionn
]
这种语法等价于以下代码
s = []
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2:
....
for itemN in iterableN:
if conditionN:
expression
下面举一些列表推导式的栗子:
a = [1,3,5,7,9,10]
b = ['a','b','c','d','e']
# a列表中所有项*2
c = [2*s for s in a]
print(c)
# 带有条件的列表推导式
d = [s for s in a if s>=5]
print(d)
# 多item
e = [(x,y) for x in a for y in b if x > 0]
print(e)
- Python中生成列表可以使用如下的表达式:
a = [x for x in range(10)]
# 0,1,2,3,4,5,6,7,8,9
- 元组或列表赋值
t = ('128',222)
a,b = t
这里a,b将被赋值为t的两个元素
等价于 a = t[0] b =t[1]
print(a)
print(b)
# 128 222
生成器
通过列表生成式,我们可以直接创建一个列表,但是受到内存的限制,列表的容量是有限的。而且我们创建的数据过于庞大会占用很大的内存空间。所以,如果列表元素可以按照某种算法推算出来,我们就可以在循环的过程中不断推算后续的元素。这样就不必创建列表。从而节省了大量的空间。在Python中,我们称这种一边循环一边计算的机制,称为生成器 generator,生成器用来创建Python序列的一个对象。它可以迭代庞大的序列,且不需要在内存中创建和存储整个序列。任何使用yield的函数都称为生成器
生成器的创建
通过列表生成式创建
要创建一个generator,我们可以把列表生成式的[]改成一个()这样就创建了一个generator
生成器表达式是一个对象,它执行的计算与列表推导相同,但会迭代地生成结果。它的语法也与列表推导式相同,但要用圆括号代替方括号
- 生成器表达式的语法
(expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
)
生成器表达式和列表推导式不同,生成器表达式实际上不创建列表或者立即对圆括号内的表达式求值。相反,它会创建一个生成器对象,该对象通过迭代并按照需要生成的值,看个栗子
a = [1, 2, 3, 4]
b = (10 * i for i in a)
print(type(b))
print(b.__next__())
print(next(b))
>>>
<class 'generator'>
10
20
**使用生成器表达式和列表推导式可以极大地提高性能和n
L = [x * x for x in range(10)]
L
g = (x*x for x in range(10))
g
# <generator object <genexpr> at 0x1022ef630>
这里L是一个list g是一个generator
使用yield
生成器本质上其实是一个函数,函数就会有返回值,我们在函数中使用yield
def gerator():
print('ok')
yield 1
print('next')
yield 2
g = gerator()
print(g)
print(next(g))
print(next(g))
######
<generator object gerator at 0x102210fc0>
ok
1
next
2
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句后返回。再次执行时从上次返回的yield语句处继续执行
我们这样定义函数后,直接执行这个函数不会被内部执行了,这个函数就会成为一个生成器对象
上面的生成器函数中,我们执行完第一个next中,程序会执行第一个yield的值,然后会记录执行位置,再执行第二个next时从上一次记录的位置继续执行,执行中遇到return,会认为函数的结束,那么生成器也会结束。
读取生成器的元素 next()
要生成的值并不在生成器中,而需要我们一个一个的去生成,可以通过next()函数获得生成器generator()的下一个返回值:
>>> next(g)
generator保存的是算法,所以每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
但是我们在使用generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心stopIteration的错误.for循环要求作用的对象是迭代对象,生成器就是一个迭代对象。所以可以直接使用for循环来取生成器的值
for i in g:
print(i)
会在内部调用生成器的next()方法
执行迭代器的函数 send(p)
生成器也是函数,可以使用send(p)执行生成器函数并传递一个函数,这个函数会传递到yield的对象中。
- 第一次使用send进入函数时只能传递一个None值,也就是说第一次send前如果没有next只能发送None数据
b.send(None)
def bar():
print('before yield')
count = yield 123
print(count)
yield 234
b = bar()
b.send(None)
b.send('abc')
Python 迭代器
在Python中,可以直接作用于for循环的数据类型可以分为:
1.集合数据类型 list、tuple、dict、set、str等
2.生成器generator 包括生成器和带yield的生成器函数
这些可以作用于for循环的对象统称为可迭代对象:Iterable
在Python中可以使用isinstance()判断一个对象是否是Iterable对象
from collections import Iterable
print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance((), Iterable))
print(isinstance('123', Iterable))
print(isinstance(100,Iterable))
#True
True
True
True
False
- 判断是否是迭代器
我们可以使用isinstance()判断一个对象是否是迭代器Iterator对象
from collections import Iterator
print(isinstance([], Iterator))
print(isinstance((), Iterator))
print(isinstance({}, Iterator))
print(isinstance('1111',Iterator))
我们可以看出list dict str tuple等数据类型虽然是Iterable,却不是Iterator,So
迭代器一定是可迭代对象,可迭代对象不一定是迭代器
那么为什么Python中 list dict str等数据类型不是Iterator?
因为Python中的Iterator对象表示的是一个数据流,而Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。这个数据流可以看做是一个有序序列。但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的的计算是惰性的,只有在需要返回下一给数据时才会计算
而Iterator可以表示一个无限大的数据流。
迭代器满足的条件:
1有iter方法
2有next方法
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
- 对于for循环,在内部所完成的三件事:
1 调用可迭代对象的iter方法返回一个迭代器对象
2 不断调用迭代器对象的next方法
3 捕获StopIterator异常