1. 程式人生 > 資訊 >上線不足一年,Twitter 宣佈將停止閱後即焚功能 Fleets

上線不足一年,Twitter 宣佈將停止閱後即焚功能 Fleets

在瞭解什麼是迭代器和生成器之前,我們先來了解一下容器的概念。對於一切皆物件來說,容器就是物件的集合。例如列表、元祖、字典等等都是容器。對於容器,你可以很直觀地想象成多個元素在一起的單元;而不同容器的區別,正是在於內部資料結構的實現方法。然後,你就可以針對不同場景,選擇不同時間和空間複雜度的容器。

所有的容器都是可迭代的。而迭代器就是可以用來遍歷容器中元素的。迭代器(iterator)提供了一個 next 的方法。呼叫這個方法後,你要麼得到這個容器的下一個物件,要麼得到一個 StopIteration 的錯誤。你不需要像列表一樣指定元素的索引,因為字典和集合這樣的容器並沒有索引一說。

對於可迭代的物件,我們可以通過iter() 函式返回一個迭代器,然後在通過next()函式可以實現遍歷。我們來看下面這段程式碼來理解一下。

s = set([1,2,3,4,5])
it = iter(s)
print(it.__next__())
print(it.__next__())
print(it.__next__())

  

它的輸出為1,2,3, 就是一個一個的輸出集合中的元素。

那什麼是生成器呢?我們知道,在迭代器中,如果我們想要列舉它的元素,這些元素需要事先生成。這裡,我們先來看下面這個簡單的樣例。

# 顯示當前 python 程式佔用的記憶體大小
def show_memory(temp):
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used: {} MB'.format(temp, memory))

def iterator():
    show_memory('initing iterator')
    list_1 = [i for i in range(10000)]
    show_memory('after iterator initiated')
    print(sum(list_1))
    show_memory('after sum called')

def generator():
    show_memory('initing generator')
    list_2 = (i for i in range(10000))
    show_memory('after generator initiated')
    print(sum(list_2))
    show_memory('after sum called')


iterator()
generator()

輸出:
initing iterator memory used: 5.58984375 MB
after iterator initiated memory used: 6.0234375 MB
49995000
after sum called memory used: 6.0234375 MB
initing generator memory used: 6.0234375 MB
after generator initiated memory used: 6.0234375 MB
49995000
after sum called memory used: 6.0234375 MB

  

iterator(),通過[i for i in range(10000)]就可以生成一個包含一萬個元素的列表。每個元素在生成後都會儲存到記憶體中,你通過程式碼可以看到,它們佔用了巨量的記憶體,記憶體不夠的話就會出現 OOM 錯誤。不過,我們並不需要在記憶體中同時儲存這麼多東西,比如對元素求和,我們只需要知道每個元素在相加的那一刻是多少就行了,用完就可以扔掉了。於是,生成器的概念應運而生,在你呼叫 next() 函式的時候,才會生成下一個變數。生成器在 Python 的寫法是用小括號括起來,(i for i in range(10000)),即初始化了一個生成器。這樣一來,你可以清晰地看到,生成器並不會像迭代器一樣佔用大量記憶體,只有在被使用的時候才會呼叫。而且生成器在初始化的時候,並不需要執行一次生成操作,相比於iterator,generator()函式節省了一次生成一萬個元素的過程。因此不需要佔用大量記憶體。

好的,瞭解了什麼是生成器和迭代器之後,我們看下面這麼一個例子:

給定一個 list 和一個指定數字,求這個數字在 list 中的位置。下面這段程式碼你應該不陌生,也就是常規做法,列舉每個元素和它的 index,判斷後加入 result,最後返回。

def index(list1, target):
    result = []
    for i, num in enumerate(list1):
        if num == target:
            result.append(i)
    return result

print(index([2, 3, 6,7,9,0,2,6], 6))

輸出:[2, 7]

  那麼使用迭代器可以怎麼做呢? 如下所示:

def index(list1, target):
    for i, num in enumerate(list1):
        if num == target:
            yield i

print(list(index([2, 3, 6,7,9,0,2,6], 6)))

輸出:[2, 7]

  

上面index函式返回的是一個生成器物件,需要轉換成list後再print輸出。

下面我們來總結一下:1.容器是可迭代物件,可迭代物件呼叫 iter() 函式,可以得到一個迭代器。迭代器可以通過 next() 函式來得到下一個元素,從而支援遍歷。

生成器是一種特殊的迭代器。使用生成器,你可以寫出來更加清晰的程式碼;合理使用生成器,可以降低記憶體佔用、提高程式速度。

有什麼問題,歡迎留言和我討論。更多硬核內容,請關注公眾號。