Python列表生成式、迭代器、生成器

三元表达式

 

 

列表生成式

我们现在有个需求,将列表[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)

 

 

迭代器

迭代是访问集合元素的⼀种⽅式。迭代器是⼀个可以记住遍历的位置的对象。迭代器对象从集合的第⼀个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

可迭代对象

  1. list、tuple、dict、set、str等
  2. generator,包括生成器和带有yield的generator function
  3. 可直接作用于for循环的对象统称为可迭代对象——Iterable

判断对象是否可迭代

迭代器对象使用

迭代器的优缺点

  1. 优点
    • 提供一种统一的、不依赖于索引的迭代方式
    • 惰性计算,节省内存
  2. 缺点
    • 无法获取长度(只有在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('烧鹿尾')

猜你喜欢

转载自blog.csdn.net/qq_28304097/article/details/80725670
今日推荐