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

1、列表生成式(List Comprehensions)

一、案例——列表每一個值加1

  先有列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求你把列表裏的每一個值加1,怎麼實現?python

方法一:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = []
for i in a:b.append(i+1)
a = b
print(a)

  此方法內存中會同時有兩份列表,所以不適合處理大規模數據。算法

方法二:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for index,i in enumerate(a):
    a[index] +=1
print(a)

方法三:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = map(lambda x:x+1, a)   # <map object at 0x1018da5f8>
print(a)

  利用map方法根據提供的函數(lambda)對指定序列作映射。express

方法四:

# 列表生成式:一行代碼完成列表操做
a = [i+1 for i in range(10)] 

二、列表生成式概念和用法

  列表生成式即List Comprehensions,是Python內置的很是簡單卻強大的能夠用來建立list的生成式。數組

# 寫列表生成式時,把要生成的元素x * x放到前面,後面跟for循環,就能夠把list建立出來
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# for循環後面還能夠加上if判斷,這樣咱們就能夠篩選出僅偶數的平方
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

# 用三元運算生成列表
>>> a = range(1,11)
>>> a = [i if i < 5 else i*i for i in a]
>>> a
[1, 2, 3, 4, 25, 36, 49, 64, 81, 100]

# 使用兩層循環,能夠生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

# 列表生成式也可使用兩個變量來生成list
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['x=A', 'y=B', 'z=C']

# 字符串操做,都變爲小寫
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

   由此能夠看到,經過列表生成式能夠直接建立一個列表。列表建立在內存中,所以列表容量受到內存限制。特別是對一個元素量很大的列表,僅需訪問前幾個元素時,尤爲浪費空間。併發

2、生成器(generator)

一、生成器定義

   列表元素能夠按照某種算法推算出來(有規律的數組),則能夠在循環的過程當中不斷推算出後續的元素。這種方式就沒必要建立完整的list,能夠節省大量的空間app

  python中,這種一邊循環一邊計算的機制,稱爲生成器:generator函數

二、生成器建立方法一:將列表生成式的‘[]’改成‘()’

  建立列表生成式和生成器的區別,僅僅是最外層的[]和()。spa

  若是要打印出生成器的元素,能夠經過nex()函數獲取generator的下一個返回值。線程

# 生成器保存的是公式,取一次建立一次,只能往前不能後退
>>> a2 = (i for i in range(1000))
>>> a2
<generator object <genexpr> at 0x103761a98>
>>> next(a2)
0
>>> next(a2)
1

#生成器走完時,會報錯:StopIteration
>>> a3 = (i for i in range(5))   # 限制5個
>>> a3
<generator object <genexpr> at 0x103761e08>
>>> next(a3)
0
>>> next(a3)
1
>>> next(a3)
2
>>> next(a3)
3
>>> next(a3)
4
>>> next(a3)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration

  生成器保存的是算法,每次調用next(g)就計算出g的下一個元素的值,直到計算出最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤。code

  建立生成器後,不多會調用next(),通常是經過for循環來迭代

a3 = (i for i in range(5))
for i in a3:   # 使用for循環來迭代生成器,不會出現StopIteration報錯,直接結束。
    print(i)
'''
0
1
2
3
4
'''

a2 = (i for i in range(3))
while True:    # while循環不適用
    next(a2)   # 報StopIteration錯誤

三、生成器建立方法二:一個函數定義中包含yield關鍵字,函數爲生成器(generator)

  例如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加獲得:1, 1, 2, 3, 5, 8, 13, 21, 34,....

  列表生成式寫不出斐波拉契數列,可是函數卻能夠輕鬆打印:

# 用函數來寫生成器,以斐波那契數列爲例
# 重點是賦值語句:a,b = b,a+b
def fib(max):  
    n, a, b = 0, 0, 1
    while n < max:
        print(b)   # 每次打印b
        a, b = b, a + b   # 0,1——>1,1——>1,2——>2,3
        n = n + 1   # n用來計數,每次自加1
    return 'done'

fib(10)

  賦值語句:a, b = b , a + b   

  至關於:t = (b, a + b)   a = t[0]   b = t[1]   t是一個tuple

  fib函數其實是定義了斐波拉契數列的推算規則,能夠從第一個元素開始,推算出後續任意的元素,這種邏輯其實很是相似generator。

>>> def fib_g(max):
...     n, a, b = 0, 0, 1
...     while n < max:
...         print('before yield')
...         yield b   # yield 把函數的執行過程凍結在這一步,而且把b的值返回給外面的next()
...         print(b)
...         a, b = b, a+b
...         n = n + 1
...     return 'done'
...
>>> f = fib_g(15)  # 將函數轉換爲生成器,有了yeild後,函數名(參數)根本不執行
>>> next(f)
before yield
1
>>> next(f)
1
before yield
1
>>> next(f)
1
before yield
2
>>> next(f)
2
before yield
3
>>> next(f)
3
before yield
5

  這種生成器和函數類似,但與函數的執行流程不一樣:

  函數是順序執行,遇到return語句或最後一行函數語句就返回。

  函數轉化爲生成器後,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。

def fib_g(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print('before yield')
        yield b   # yield 把函數的執行過程凍結在這一步,而且把b的值返回給外面的next()
        # print(b)
        a, b = b, a+b
        n = n + 1
    return 'done'
# 生成器使用意義:能夠將函數執行中的狀態、數據返回到外部來

data = fib_g(10)
print(data)

print(data.__next__())
print(data.__next__())
print("乾點別的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())

"""
<generator object fib_g at 0x1014670f8>
1
1
乾點別的事
2
3
5
8

"""

  在上例中,循環過程當中不斷調用yield,就會不斷中斷。並且須要設置循環退出條件,不然會產生無限數列。

  將函數改寫爲生成器後,一般不會用next()來獲取下一個值,而是使用for循環來迭代。

def fib_g(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b   # yield 把函數的執行過程凍結在這一步,而且把b的值返回給外面的next()
        a, b = b, a+b
        n = n + 1
    return 'done'

for n in fib_g(6):
    print(n)

"""
1
1
2
3
5
8
"""

  for循環調用生成器,拿不到生成器的return語句的返回值,要拿到返回值,必須捕獲StopIteration錯誤。

def fib_g(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b   # yield 把函數的執行過程凍結在這一步,而且把b的值返回給外面的next()
        a, b = b, a+b
        n = n + 1
    return 'done'

g = fib_g(6)
while True:
    try:
        x = next(g)
        print('g', x)
    except StopIteration as e:
        print('Generator return value:', e.value)
        break

"""
g 1
g 1
g 2
g 3
g 5
g 8
Generator return value: done
"""

四、生成器send方法

send方法做用:
  一、喚醒並繼續執行  (next方法只有這個功能)
  二、發送一個信息到生成器內部

def range2(n):

    count = 0
    while count < n:
        print('count', count)
        count += 1
        sign = yield count
        if sign == 'stop':
            break
        print("---sign", sign)
    return 3333 


new_range = range2(3)  # 0,1,2
n1 = next(new_range)
print(new_range)
new_range.send("stop")  # 生成器再也不執行後兩步直接終止

"""
count 0
<generator object range2 at 0x10244c0f8>
Traceback (most recent call last):
  File "/Users/.../函數生成器2.py", line 20, in <module>
    new_range.send("stop")
StopIteration: 3333
"""

   以下所示爲生成器與外部交互:

def run():
    count = 0
    while True:
        n = yield count
        print("--", n, count)
        count += 1


g =run()

g.__next__()
g.send("alex")
g.send("egon")
g.send("jack")
"""
-- alex 0
-- egon 1
-- jack 2
"""

五、經過yield實現單線程狀況下實現併發運算的效果

#_*_coding:utf-8_*_
__author__ = 'Alex Li'

import time
def consumer(name):
    print("%s 準備吃包子啦!" %name)
    while True:
       baozi = yield

       print("包子[%s]來了,被[%s]吃了!" %(baozi,name))


def producer(name):
    c = consumer('A')
    c2 = consumer('B')
    c.__next__()
    c2.__next__()
    print("老子開始準備作包子啦!")
    for i in range(10):
        time.sleep(1)
        print("作了2個包子!")
        c.send(i)
        c2.send(i)

producer("alex")

經過生成器實現協程並行運算

3、生成器總結

一、生成器的建立方式   

  1.列表 列表生成式
  2.函數 函數生成器 yield

二、yield 對比 return

  return返回並停止function
  yield返回函數,並凍結當前的執行過程
  函數有了yield以後
  (1)函數名加()就變成了一個生成器
  (2)return在生成器裏,表明生成器的停止,直接報錯

三、next:喚醒生成器並繼續執行

  next喚醒凍結的函數執行過程,繼續執行,直到遇到下一個yield

四、send("stop"):

  1.喚醒並繼續執行
  2.發送一個信息到生成器內部

五、python2和python3中range()方法對比

在Python2中:
  range = list

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  xrange = 生成器

>>> a = xrange(10)
>>> type(a)
<type 'xrange'>
>>> for i in a:
...     print i
...
0
1
2
3
4
5
6
7
8
9

在Python3中:
  range = genetator

>>> range(10)  # range(0,10)  生成這個公式沒有正式建立
range(0, 10)
>>> type(range(10))
<class 'range'>

  xrange :python3中已取消該方法

4、可迭代對象(Iterable)和迭代器(Iterator)

一、什麼是可迭代對象?什麼是迭代器?

  可直接做用於for循環的數據類型有一下幾種:

  1、集合數據類型,如:list、tuple、dict、set、str等;

  2、generator,包括生成器表達式(geneator expression)和生成器函數(generator function)兩組構建方式。

  上述這些能夠直接做用於for循環的對象統稱爲可迭代對象(Iterable)。可使用isinstance()判斷一個對象是不是Iterable對象。

from collections import Iterable  # 可迭代類型
# 使用isinstance()判斷一個對象是不是可迭代對象
isinstance('abc', Iterable)
isinstance({}, Iterable)
isinstance((x for x in range(10)), Iterable)

isinstance(100, Iterable)  # 返回False

   迭代器定義:能夠被next()函數調用並不斷返回下一個值得對象稱爲迭代器(Iterator)

二、可迭代對象與迭代器的關係

生成器是迭代器的一種:

  一、生成器都是迭代器對象,但list\dict\str雖然是可迭代對象,但不是迭代器。

  二、把list\dict\str等可迭代對象變成迭代器可使用iter()函數

>>> from collections import Iterator
>>> isinstance('abc', Iterator)  
False
>>> a = iter('abc')
>>> a
<str_iterator object at 0x10c584b38>
>>> a.__next__()
'a'
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

三、爲何listdictstr等數據類型不是Iterator

  這是由於Python的Iterator對象表示的是一個數據流,Iterator對象能夠被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。能夠把這個數據流看作是一個有序序列,但咱們卻不能提早知道序列的長度,只能不斷經過next()函數實現按需計算下一個數據,因此Iterator的計算是惰性的,只有在須要返回下一個數據時它纔會計算。

  Iterator甚至能夠表示一個無限大的數據流,例如全體天然數。而使用list是永遠不可能存儲全體天然數的。

四、總結

  凡是可做用於for循環的對象都是Iterable類型;

  凡是可做用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;

  集合數據類型如listdictstr等是Iterable但不是Iterator,不過能夠經過iter()函數得到一個Iterator對象。

  Python的for循環本質上就是經過不斷調用next()函數實現的。 

for x in [1, 2, 4, 5]:
    pass

# 徹底等價於
# 得到Iterataor對象
it = iter([1, 2, 3, 4, 5])
# 循環
while True:
    try:
        # 得到下一個值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循環
        break
相關文章
相關標籤/搜索