1.合并追加到列表聲明
我們從一個簡單的開始。不是聲明一個空列表然后附加到它,而是直接用所有元素初始化列表。這縮短了代碼并使意圖更加明確。它的性能也稍微好一些,因為它避免了對 append() 的函數調用。
players = []
players.append("Patrick")
players.append("Max")
players.append("Jessi")
# -> refactor
players = ["Patrick", "Max", "Jessi"]
這同樣適用于填充其他集合類型,如集合和字典。
2 使用items()直接解包字典值
當遍歷字典時,你需要鍵和值,那么不要手動訪問值。而是迭代dictionary.items(),它同時為你提供鍵和值。
這節省了我們過去分配給 players 的行,代碼現在讀起來更自然,重復更少。
teams_by_color = {"blue": ["Patrick", "Jessi"]}
for team_color in teams_by_color:
players = teams_by_color[team_color]
if is_winning(team_color):
advance_level(players)
# -> refactor
for team_color, players in teams_by_color.items():
if is_winning(team_color):
advance_level(players)
3. 將 range(len) 替換為枚舉
如果我們需要遍歷列表并且需要同時跟蹤索引和當前項,請使用內置enumerate()?函數而不是range(len)。這會將當前索引和當前項目作為元組返回。所以我們可以直接在這里查看值,也可以訪問帶有索引的項目。
for i in range(len(players)):
print(i, players[i])
# -> refactor
for i, player in enumerate(players):
print(i, player)
Enumerate? 還帶有一個可選的start參數。如果你使用它,計數器將從該值開始。但請注意,這些項目仍然從第一個開始。
for i, player in enumerate(players, start=1):
print(i, player)
4. 用枚舉調用替換手動循環計數器
這與之前非常相似。有時我會看到直接對項目執行迭代的代碼——這本身并不壞——但隨后需要一個計數器,它會在循環內手動遞增。同樣在這里你可以簡單地使用 enumerate 函數。這更簡單,也更快。
i = 0
for player in players:
print(i, player)
i += 1
# -> refactor
for i, player in enumerate(players):
print(i, player)
4.1 不要手動更新計數器
如果你只需要計算項目的數量,也不要遍歷循環并手動計算所有項目。相反,只需使用len()函數來獲取列表中的元素數。
num_players = 0
for player in players:
num_players += 1
# -> refactor
num_players = len(players)
5.將條件簡化為return語句
當我們到達一個方法的末尾并想要返回 True? 或 False? 時,一種常見的做法是這樣的。如果條件為 True?,我們返回 True?。否則我們最后返回 False。然而,直接返回結果更簡潔:
def function():
if isinstance(a, b) or issubclass(b, a):
return True
return False
# -> refactor
def function():
return isinstance(a, b) or issubclass(b, a)
我們在這里應該注意的一件事是,只有當表達式的計算結果為布爾值時才能這樣做。isinstance()和issubclass()都是返回布爾值的函數,所以這很好。但在下一個示例中,第一個表達式pythonistas是一個列表而不是布爾值。
如果pythonistas?是一個有效的非空列表,這將返回列表而不是預期的布爾值,然后可能是你的應用程序中的錯誤。因此,為了確保我們在這里返回一個布爾值,我們可以將返回包裝在對 bool()函數的調用中。
def any_pythonistas():
pythonistas = [coder for coder in coders if is_good_in_python(coder)]
if pythonistas or self.is_pythonista():
return True
return False
# -> refactor
def any_hats():
pythonistas = [coder for coder in coders if is_good_in_python(coder)]
return bool(pythonistas or self.is_pythonista())
6.合并條件中的重復塊
我們應該始終尋找機會刪除重復的代碼。這樣做的好地方是if …elif鏈中有多個相同的塊。
在此示例中,if? 和 elif? 都導致相同的執行功能。所以我們可以使用or?組合前兩個塊來刪除對函數的重復調用?,F在,如果我們需要更改process_standard_payment()行,我們可以在一處而不是兩處進行。
def process_payment(payment, currency):
if currency == "USD":
process_standard_payment(payment)
elif currency == "EUR":
process_standard_payment(payment)
else:
process_international_payment(payment)
# -> refactor
def process_payment(payment, currency):
if currency == "USD" or currency == "EUR":
process_standard_payment(payment)
else:
process_international_payment(payment)
7.用in運算符替換同一個變量的多次比較
我們甚至可以進一步重構以前的代碼。由于我們針對多個值重復檢查同一個變量,我們可以使用 in 運算符來縮短它。如果貨幣值在定義的列表中,我們將執行專用操作。
def process_payment(payment, currency):
if currency == "USD" or currency == "EUR":
process_standard_payment(payment)
else:
process_international_payment(payment)
# -> refactor
def process_payment(payment, currency):
if currency in ["USD", "EUR"]:
process_standard_payment(payment)
else:
process_international_payment(payment)
為了再次改進這一點,我們應該在這里使用一個集合。在集合中查找值更快,而且無論如何我們都想要這里的唯一元素,所以集合是更好的選擇。
# -> refactor
def process_payment(payment, currency):
if currency in {"USD", "EUR"}:
process_standard_payment(payment)
else:
process_international_payment(payment)
8. 將 for 循環中的 yield 替換為 yield from
如果你已經熟悉生成器,那么這是一個高級技巧。一個經常被忽略的小技巧是 Python 的 yield? 關鍵字對于可迭代對象有一個對應的yield from。
如果你有一個像列表這樣的可迭代對象,而不是說for item in iterable: yield item?,你可以簡單地說yield from iterable。這更短,并且消除了對可迭代對象的手動循環,這也可以提高性能。
def get_content(entry):
for block in entry.get_blocks():
yield block
# -> refactor
def get_content(entry):
yield from entry.get_blocks()