渣翻
文章來自Randy Daw-Ran Liou : Never Write For-Loops Again.
作者本人Blog
正文如下
這是一個挑戰(zhàn)读第,我挑戰(zhàn)你去避免在每一個場景下寫for循環(huán)。并且肌索,我挑戰(zhàn)你去尋找那些很難寫出除了for循環(huán)之外的其他代碼的場景,請分享出來憎瘸。我很愿意去了解它們头遭。
我開始探索Python語言中一些令人驚異的功能已經(jīng)有一段時間了。在初始階段叠荠,這只是一個我挑戰(zhàn)我自己去使用更多的語言特性去代替我從其他語言上學(xué)習(xí)到的使用方法。然后事情變得很有趣扫责。不僅僅是代碼變得更加簡短和干凈榛鼎,并且代碼看起來更加的有結(jié)構(gòu)和有規(guī)律。我將會在這篇文章中更深入這些益處鳖孤。
但是者娱,首先我們要退后一步去了解一下寫一個for循環(huán)的直觀原因:
- 通過遍歷一個序列得到一些信息
- 通過當(dāng)前序列生成一個新的序列
- 因為我是一個程序員,所以我很自然地去寫for循環(huán)
幸運的是苏揣,在Python的內(nèi)置模塊中已經(jīng)有很多很好的工具去幫助你達到這樣的目標(biāo)黄鳍!你要做的只是改變你的思維去從另外一個角度理解。
你能在不寫for循環(huán)中得到什么平匈?
- 更少的代碼行數(shù)
- 更好的代碼可讀性
- 僅僅讓縮進管理代碼文本
讓我們先看下面的代碼結(jié)構(gòu)
# 1
with ...:
for ...:
if ...:
try:
except:
else:
在這個示例中框沟,我們正在處理很多層的代碼,這很難去閱讀增炭。我所發(fā)現(xiàn)的問題是這段代碼通過給定的無處不在的縮進混合了管理邏輯(with
和try-except
)以及業(yè)務(wù)邏輯(for
忍燥、if
)。如果你只使用縮進去規(guī)范管理邏輯隙姿,那么你的業(yè)務(wù)就可以脫離出來梅垄。
"扁平優(yōu)于嵌套" - Python之禪
你可以用來避免使用for循環(huán)的工具
一、列表推導(dǎo)式 / 生成器表達式
我們可以先看這樣一個簡單的示例输玷。你想要在一個已存在的序列上得到一個新的序列
result = []
for item in item_list:
new_item = do_something_with(item):
result.append(item)
如果你喜歡MapReduce的話可以使用map
或者Python擁有列表推導(dǎo)式队丝。代碼如下:
result = [do_something_with(item) for item in item_list]
同樣的如果你想要得到一個生成器,你可以使用生成器表達式饲嗽,他們的語法很類似炭玫。(你怎么能不喜愛Python的一致性呢?)代碼如下:
result = (do_something_with(item) for item in item_list)
二貌虾、 函數(shù)
從一個更高階,更實用的編碼方式裙犹,如果你想要映射一個序列到另一個序列尽狠,只需要使用map
函數(shù)衔憨。(從我的理解,可以使用列表推導(dǎo)式代替它)
doubled_list = map(lambda x: x * 2, old_list)
如果你想要將一個序列生一個最終結(jié)果袄膏〖迹可以使用reduce
。
from functools import reduce
summation = reduce(lambda x, y: x + y, numbers)
另外沉馆,很多Python的內(nèi)置方法都可以使用迭代器(iterables):
In [1]: a = list(range(10))
In [2]: a
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [3]: all(a)
Out[3]: False
In [4]: any(a)
Out[4]: True
In [5]: max(a)
Out[5]: 9
In [6]: min(a)
Out[6]: 0
In [7]: list(filter(bool, a))
Out[7]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
In [8]: set(a)
Out[8]: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
In [9]: dict(zip(a, a))
Out[9]: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
In [10]: sorted(a, reverse=True)
Out[10]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
In [11]: str(a)
Out[11]: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'
In [12]: sum(a)
Out[12]: 45
...........
三码党、提取函數(shù)或者生成器
上面兩個方法很好的處理了簡單的邏輯。那么怎么去處理復(fù)雜的邏輯呢斥黑?作為一個程序員揖盘,我們經(jīng)常會把困難的事情抽象成函數(shù)。同樣的方法也適用與這里锌奴。如果你寫下面這樣的代碼:
results = []
for item in item_list:
# setups
# condition
# processing
# calculation
result.append(result)
顯然的你讓一段代碼承擔(dān)了太多的責(zé)任兽狭。取而代之的,我建議你這樣做:
do process_item(item):
# setups
# conditions
# processing
# calculation
return result
results = [process_item(item) for item in item_list]
嵌套循環(huán)是怎么樣的呢鹿蜀?
results = []
for i in range(10):
for j in range(i):
results.appens((i, j))
列表表達式這樣幫助你:
results = [(i, j)
for i in range(10)
for j in range(i)]
如果你想在代碼中保存一些內(nèi)部的狀態(tài)怎么辦呢箕慧?
# finding the max prior to the current item
a = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
results = []
current_max = 0
for i in a:
current_max = max(i, current_max)
results.append(current_max)
# results = [3, 4, 6, 6, 6, 9, 9, 9, 9, 9]
讓我們抽象成一個生成器去做到這樣的功能:
def max_generatir(numbers):
current_max = 0
for i in numbers:
current_max = max(i, current_max)
yield current_max
a = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
results = list(max_generator(a))
Oh! 等一下, 你剛剛在代碼段中使用了一個for循環(huán)茴恰, 這是欺騙颠焦!
好啦,自作聰明的家伙往枣,讓我們試一下下面的蒸健。
四、不要自己寫婉商,itertools
已經(jīng)寫了
這個模塊(itertools
)簡直精彩極了似忧。我相信這個模塊實現(xiàn)了80%的你想寫for循環(huán)的情況。舉個栗子丈秩, 剛才的最后一個示例可以被這樣重寫:
from itertools import accumulate
a = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
results = list(accumulate(a, max))
當(dāng)然盯捌,如果你要迭代一個組合的序列, 你可以使用product()
蘑秽、permutations()
饺著、combinations()
。
結(jié)論
- 你在大多數(shù)場景下是不需要寫for循環(huán)的
- 為了擁有更好的代碼可讀性肠牲,你應(yīng)當(dāng)避免使用for循環(huán)
行動
- 再次查看你的代碼幼衰,找到那些你憑借直覺寫for循環(huán)的地方。再次思考一下缀雳,看看是否可以不使用for循環(huán)去重寫它渡嚣。
- 分享你的那些很難不使用for循環(huán)的例子。
--
菜鳥級別的翻譯。识椰。見諒绝葡。嘻嘻