三元表达式
列表生成式
我们现在有个需求,将列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]中每个数字都加1,你能想到几种解决方案?
NO1. 普通青年版
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = []
for i in a:
b.append(i + 1)
a = b
print("a = ", a)
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for index, i in enumerate(a):
a[index] += 1
print("a = ", a)
NO2. 文艺青年版
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a = map(lambda x: x + 1, a)
print("list[a] = ", list(a))
N03. 装X青年版
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a = [i + 1 for i in range(1, 10)]
print("a = ", a)
迭代器
迭代是访问集合元素的⼀种⽅式。迭代器是⼀个可以记住遍历的位置的对象。迭代器对象从集合的第⼀个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象
- list、tuple、dict、set、str等
- generator,包括生成器和带有yield的generator function
- 可直接作用于for循环的对象统称为可迭代对象——Iterable
判断对象是否可迭代
迭代器对象使用
迭代器的优缺点
- 优点
- 提供一种统一的、不依赖于索引的迭代方式
- 惰性计算,节省内存
- 缺点
- 无法获取长度(只有在next完毕才知道到底有几个值)
- 一次性的,只能往后走,不能往前退
生成器
What is 生成器?
通过列表⽣成式,我们可以直接创建⼀个列表。但是,受到内存限制,列表容量肯定是有限的。⽽且,创建⼀个包含100万个元素的列表,不仅占⽤很⼤的存储空间,如果我们仅仅需要访问前⾯⼏个元素,那后⾯绝⼤多数元素占⽤的空间都⽩⽩浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从⽽节省⼤量的空间。在Python中,这种⼀边循环⼀边计算的机制,称为⽣成器:generator。
NO1. 将一个列表生成式的[]改成(),就创建了一个generator
>>> L = [x ** 2 for x in range(5)]
>>> L
[0, 1, 4, 9, 16]
>>>
>>> G = (x ** 2 for x in range(5))
>>> G
<generator object <genexpr> at 0x104516828>
>>> G.__next__()
0
>>> next(G)
1
>>> for i in G:
... print(i)
...
4
9
16
NO2. generator⾮常强⼤。如果推算的算法⽐较复杂,⽤类似列表⽣成式的 for 循环⽆法实现的时候,还可以⽤函数来实现。
⽐如,著名的斐波拉契数列(Fibonacci),除第⼀个和第⼆个数外,任意⼀个数都可由前两个数相加得到
1, 1, 2, 3, 5, 8, 13, 21, 34, …
斐波拉契数列⽤列表⽣成式写不出来,但是,⽤函数把它打印出来却很容易。
仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第⼀个元素开始,推算出后续任意的元素,这种逻辑其实⾮常类似generator。
也就是说,上⾯的函数和generator仅⼀步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了。
In [4]: def fib(max):
...: n = 0
...: a = 0
...: b = 1
...: while n < max:
...: yield b
...: a = b
...: b = a + b
...: n += 1
...: return 'done'
...:
...:
In [5]: f = fib(5)
In [6]: next(f)
Out[6]: 1
In [7]: next(f)
Out[7]: 2
In [8]: next(f)
Out[8]: 4
In [9]: next(f)
Out[9]: 8
In [10]: next(f)
Out[10]: 16
In [11]: next(f)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-11-aff1dd02a623> in <module>()
----> 1 next(f)
StopIteration: done
但是⽤for循环调⽤generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中。
只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码。
自定义函数模拟range(1,7,2)
send()
def eater(name):
print('%s 准备开始吃饭啦' % name)
my_food = None
while True:
food = yield None
print('%s 吃了 %s' % (name, food))
g = eater('Allen')
print(next(g))
g.send(None) #对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g)
g.send('蒸羊羔')
g.send('蒸鹿茸')
g.send('蒸熊掌')
g.send('烧素鸭')
#g.close()
g.send('烧素鹅')
g.send('烧鹿尾')