列表/元組/字典/集合都是容器。對於容器,可以很直觀地想象成多個元素在一起的單元;而不同容器的區別,正是在於內部資料結構的實現方法。
所有的容器都是可迭代的(iterable)。另外字串也可以被迭代。
迭代可以想象成是你去買蘋果,賣家並不告訴你他有多少庫存。這樣,每次你都需要告訴賣家,你要乙個蘋果,然後賣家採取行為:要麼給你拿乙個蘋果;要麼告訴你,蘋果已經賣完了。你並不需要知道,賣家在倉庫是怎麼擺放蘋果的。
嚴謹地說,迭代器(iterator)
提供了乙個next
(可以不重複不遺漏地乙個乙個拿到所有元素) 的方法。呼叫這個方法後,你要麼得到這個容器的下乙個物件,要麼得到乙個stopiteration
的錯誤(蘋果賣完了)。
示例,判斷是否可迭代
from collections.abc import iterable
params = [
1234,
'1234',
[1, 2, 3, 4],
set([1, 2, 3, 4]),
,(1, 2, 3, 4)
]
for param in params:
print('{} is iterable? {}'.format(param, isinstance(param, iterable)))
# 輸出
# 1234 is iterable? false
# 1234 is iterable? true
# [1, 2, 3, 4] is iterable? true
# is iterable? true
# is iterable? true
# (1, 2, 3, 4) is iterable? true
生成器可以想象成是你去買蘋果,賣家並沒有庫存。這樣,每次你都需要告訴賣家,你要乙個蘋果,然後賣家採取行為,立馬生成 1 個蘋果(生成速度極快):要麼給你拿乙個蘋果;要麼告訴你,蘋果已經賣完了。
示例,迭代器與生成器的對比
import os
import psutil
import time
import functools
def log_execution_time(func):
@functools.wraps(func)
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
return res
# 顯示當前 python 程式占用的記憶體大小
def show_memory_info(hint):
pid = os.getpid()
p = psutil.process(pid)
info = p.memory_full_info()
memory = info.uss / 1024. / 1024
print('{} memory used: {} mb'.format(hint, memory))
@log_execution_time
def test_iterator():
show_memory_info('initing iterator')
list_1 = [i for i in range(100000000)]
show_memory_info('after iterator initiated')
print(sum(list_1))
show_memory_info('after sum called')
@log_execution_time
def test_generator():
show_memory_info('initing generator')
list_2 = (i for i in range(100000000))
show_memory_info('after generator initiated')
print(sum(list_2))
show_memory_info('after sum called')
test_iterator()
print()
test_generator()
########## 輸出 ##########
# initing iterator memory used: 10.16796875 mb
# after iterator initiated memory used: 3664.34765625 mb
# 4999999950000000
# after sum called memory used: 3664.34765625 mb
# test_iterator took 6179.794754018076 ms
# initing generator memory used: 19.140625 mb
# after generator initiated memory used: 19.14453125 mb
# 4999999950000000
# after sum called memory used: 19.171875 mb
# test_generator took 4912.561981996987 ms
迭代器是乙個有限集合
,生成器則可以成為乙個無限集
。
我們並不需要在記憶體中同時儲存這麼多東西,比如對元素求和,我們只需要知道每個元素在相加的那一刻是多少就行了,用完就可以扔掉了。
於是,生成器的概念應運而生,在你呼叫next()
函式的時候,才會生成下乙個變數。生成器在 python 的寫法是用小括號括起來,(i for i in range(100000000))
,即初始化了乙個生成器。
這樣一來,你可以清晰地看到,生成器並不會像迭代器一樣占用大量記憶體,只有在被使用的時候才會呼叫。而且生成器在初始化的時候,並不需要執行一次生成操作,相比於test_iterator()
,test_generator()
函式節省了一次生成一億個元素的過程,因此耗時明顯比迭代器短。
示例,數學中有乙個恒等式,(1 + 2 + 3 + ... + n)^2 = 1^3 + 2^3 + 3^3 + ... + n^3 的證明
def generator(k):
i = 1
while true:
yield i ** k
i += 1
gen_1 = generator(1)
gen_3 = generator(3)
print(gen_1)
print(gen_3)
def get_sum(n):
sum_1, sum_3 = 0, 0
for i in range(n):
next_1 = next(gen_1)
next_3 = next(gen_3)
print('next_1 = {}, next_3 = {}'.format(next_1, next_3))
sum_1 += next_1
sum_3 += next_3
print(sum_1 * sum_1, sum_3)
get_sum(8)
########## 輸出 ##########
# # # next_1 = 1, next_3 = 1
# next_1 = 2, next_3 = 8
# next_1 = 3, next_3 = 27
# next_1 = 4, next_3 = 64
# next_1 = 5, next_3 = 125
# next_1 = 6, next_3 = 216
# next_1 = 7, next_3 = 343
# next_1 = 8, next_3 = 512
# 1296 1296
接下來的 yield 是魔術的關鍵。對於初學者來說,你可以理解為,函式執行到這一行的時候,程式會從這裡暫停,然後跳出,不過跳到**呢?答案是next()
函式。那麼i ** k
是幹什麼的呢?它其實成了next()
函式的返回值。這樣,每次 next(gen) 函式被呼叫的時候,暫停的程式就又復活了,從 yield 這裡向下繼續執行;同時注意,區域性變數 i 並沒有被清除掉,而是會繼續累加。我們可以看到 next_1 從 1 變到 8,next_3 從 1 變到 512。
示例,給定兩個序列,判定第乙個是不是第二個的子串行。
leetcode 鏈結如下:
先來解讀一下這個問題本身。序列就是列表,子串行則指的是,乙個列表的元素在第二個列表中都按順序出現,但是並不必挨在一起。舉個例子,[1, 3, 5] 是 [1, 2, 3, 4, 5] 的子串行,[1, 4, 3] 則不是。
def is_subsequence(ls, sub):
ls = iter(ls)
return all(i in ls for i in sub)
print(is_subsequence([1, 2, 3, 4, 5],[1, 3, 5]))
print(is_subsequence([1, 2, 3, 4, 5],[1, 4, 3]))
########## 輸出 ##########
# true
# false
Python高階之迭代器和生成器
python中任意的物件,只要它定義了可以返回乙個迭代器的 iter 方法,或者定義了可以支援下標索引的 getitem 方法,那麼它就是乙個可迭代物件。簡單來說,可迭代物件就是能提供迭代器的任意物件,但可迭代物件本身並不一定是乙個迭代器。任意物件,只要定義了next python2 或者 next...
Python 高階特性 生成器和迭代器
生成器generator 目的 list容量太大佔太多記憶體而操作時卻僅僅訪問前幾個元素,generator可以通過迴圈不斷推算出後面的元素,邊迴圈邊計算,節省大量空間 建立乙個generator vlist a a for a in range 3 vlist 0,1,4 vgenerator b...
python高階 迭代器 生成器
迭代是訪問集合元素的一種方式。迭代器是乙個可以記住遍歷的位置的物件。迭代器物件從集合的第乙個元素開始訪問,直到所有 list tuple等都是可迭代物件,我們可以通過iter 函式獲取這些可迭代物件的迭代器。然後我們可以對獲取到的迭代器不斷使用next 函式來獲取下一條資料。iter 函式實際上就是...