首頁歷史 > 正文

Python中5種常見的反模式

2022-01-21由 為AI吶喊 發表于 歷史

Python 是2021年最流行的程式語言之一。

簡單的語法使它成為很多程式設計師的入門首選。

由於 Python 具有動態性和靈活性,有時候開發人員很容易編寫出錯誤及低效的程式碼。

本文將向大家介紹 Python 中常見的反模式,並給出了更好的編譯方法。

1。對Iterable物件使用map()和filter()

內建的 map 和 filter 可以幫助我們透過函式程式設計的原理在 Python 中轉換 iterable 物件。

這兩個方法都接受一個函式和一個 iterable 作為引數,並返回相應的物件。

透過將該物件作為引數傳遞到 Python 中的內建列表建構函式,可以將其轉換為列表。

我們經常使用 lambda 函式作為 map、filter 函式的引數:

my_list = [1, 2, 3, 4 ,5, 6, 7, 8, 9, 10]# 將每個元素乘以2print(list(map(lambda x: x * 2, my_list))) # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]# 過濾掉偶數print(list(filter(lambda x: x % 2 == 0, my_list))) # [2, 4, 6, 8, 10]

上面的程式碼看起來相當累贅和不清楚。使用列表理解可以實現相同結果:

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]# 與map相同print([x * 2 for x in my_list])# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]# 與filter相同print([x for x in my_list if x % 2 == 0])# [2, 4, 6, 8, 10]

不使用lambda函式後,列表理解變得更具可讀性和簡潔性。

2。輸入較大時使用列表理解

列表理解有助於我們編寫出清晰、簡潔的程式碼。

但是,列表理解總是為 iterable 中的每個值建立一個列表。當輸入量非常大時,就會導致記憶體佔用過大的問題:我們的機器可能會崩潰。

生成器表示式

結合了列表理解和生成器這兩個方面的優點,為處理大型輸入序列提供了更有效的方法。

要建立生成器表示式,只需將列表中的 [] 方括號替換為()方括號。

Python中5種常見的反模式

生成器表示式並不是建立一個全新的列表,而是建立一個迭代器。

這會降低建立速度並最佳化記憶體分配。我們可以使用 next 函式或透過迴圈訪問生成器表示式的每個後續元素。

my_list = [1, 2, 3, 4 ,5, 6, 7, 8, 9, 10]my_gen_expr = (x * 2 for x in my_list)print(next(my_gen_expr))print(next(my_gen_expr))# >># 2# 4for x in my_gen_expr: print(x)# >># 6# 8# 10# 12# 14# 16# 18# 20

注:生成器表示式是有狀態的,因此在重用時要注意。如果要多次使用迭代器,則可能需要重新建立迭代器。

3。不使用range()的情況

range 函式對迭代整數很有用。

for i in range(10): print(i)

當迭代類似列表的資料結構時,我們可以完全依賴for迴圈語法來訪問每個專案。程式碼如下:

my_list = [2, 4, 6, 8, 10]for item in my_list: print(item)# Output:# 2# 4# 6# 8# 10

但是,當想要訪問索引和元素時,我們可以使用列表長度下的 range 方法,如下所示:

my_list = [2, 4, 6, 8, 10]for i in range(len(my_list)): print(“index: ”, i, “value: ”, my_list[i]) # Output:# index: 0 value: 2# index: 1 value: 4# index: 2 value: 6# index: 3 value: 8# index: 4 value: 10

程式碼看起來不可讀,因為我們必須在列表上呼叫 len,然後使用 range 方法包裝輸出。為了使程式碼更加具有 python 風格,我們必須提高程式碼的可讀性。

更好的方法是對 list 物件呼叫 enumerate 函式。這將建立一個生成器,生成列表項的索引和值。

my_list = [2, 4, 6, 8, 10]for i, v in enumerate(my_list): print(“index: ”, i, “value: ”, v) # Output:# index: 0 value: 2# index: 1 value: 4# index: 2 value: 6# index: 3 value: 8# index: 4 value: 10

程式碼看起來是不是更加乾淨了?

4。字典中鍵丟失的問題

字典具有快速訪問、分配、插入和刪除的能力,是一種非常流行的資料結構。

但是新手開發人員訪問字典中不存在的金鑰時經常會遇到問題。

crypto_price = { “Bitcoin”: 64000, “Ethereum”: 2300, “Dogecoin”: 0。12}crypto_price[“XRP”]

Python中5種常見的反模式

處理該類問題的其中一種方法是

檢查字典中是否存在金鑰

,程式碼如下:

key = “XRP”if key not in crypto_price: crypto_price[key] = 1。2 print(crypto_price[key])

另一種方法是使用 try/except 塊,如下所示:

key = “XRP”try: xrp = crypto_price[key]except raise KeyError: xrp = 1。2 crypto_price[key] = xrp

上面的程式碼確實實現了我們的目標,但是我們可以透過使用字典方法 get 進一步改進。

透過使用 get 方法來獲取相應鍵的值,而不是使用方括號 [] 來訪問字典的鍵。

另外,如果鍵不存在,get 方法將返回 None,而不是丟擲 KeyError。如果缺少鍵而不是無鍵,還可以將引數傳遞給 get 方法以獲取預設值。

key = “XRP”if crypto_price。get(“XRP”) is None: crypto_price[“XRP”] = 1。2

ada = crypto_price。get(“ADA”, 0)# Prints 0print(ada)

5。惰性關鍵字和位置引數設計

Python函式能夠同時接受位置引數和關鍵字引數。

位置引數是不後跟等號(=)和預設值的名稱。

關鍵字引數後面跟一個等號和一個給出其預設值的表示式。

得益於這種設計,python函式的建立和重用非常靈活。

但是,定義函式時,錯誤的設計選擇可能會導致程式碼中難以修復的錯誤。

我們以計算複利的函式為例:

# 複利計算器年/月複利def calculate_compound_interest(principal, rate, time_in_years, compounded_monthly, to_string): t = 1 if compounded_monthly: t = 12 amt = principal * (1 + rate/(t * 100)) ** (time_in_years * t) if to_string: return f“${amt - principal:。2f}” return amt - principal calculate_compound_interest(100, 5, 2, False, False) # 10。25

呼叫函式時出現的一個問題是,兩個布林引數(compounded_monthly 和結尾的 to_string)很容易相互混淆。這就會出現難以追蹤的問題。

我們可以透過如下方式更改函式定義來提高可讀性:

# 複利計算器年/月複利def calculate_compound_interest(principal, rate, time_in_years, compounded_monthly=False, to_string=False):

透過將兩個布林引數指定為關鍵字引數,函式呼叫方可以顯式地指定要設定的布林值,這些值將覆蓋預設值。

calculate_compound_interest(100, 5, 2, compounded_monthly=True)# 10。49413355583269calculate_compound_interest(100, 5, 2, to_string=True)# ‘$10。25’

但是,這仍然會出現問題,主要原因是關鍵字引數是可選的,因為沒有任何強制呼叫方將這些作為關鍵字引數使用。

因此,我們仍然可以使用舊方法呼叫該函式:

calculate_compound_interest(100, 5, 2, False, False)

解決該問題的方法是僅在定義函式時強制布林引數為關鍵字:

# 複利計算器年/月複利def calculate_compound_interest(principal, rate, time_in_years, *, # Changed compounded_monthly=False, to_string=False):

我們看到,*符號表示位置引數的結束和僅關鍵字引數的開始。

如果這樣呼叫:

calculate_compound_interest(100, 5, 2, False, False)

將發生以下錯誤:

——————————————————————————————————————-TypeError Traceback (most recent call last) in ——> 1 print(calculate_compound_interest(1000, 5, 2, False, False))TypeError: calculate_compound_interest() takes 3 positional arguments but 5 were given

但是,關鍵字引數及其預設行為仍將保持不變,如下所示:

alculate_compound_interest(100, 5, 2, compounded_monthly=True)# 10。49413355583269calculate_compound_interest(100, 5, 2, to_string=True)# ‘$10。25’

然而,仍然存在一個問題。

假設呼叫者決定對前三個必需引數(principal、rate、time in years)混合使用位置和關鍵字。

如果這三個引數的函式引數名稱發生更改,我們將看到Python直譯器。它會這樣說:

# 複利計算器年/月複利def calculate_compound_interest(p, r, t_in_y, *, # Changed compounded_monthly=False, to_string=False):

calculate_compound_interest(principal=1000, rate=5, time_in_years=2)calculate_compound_interest(1000, 5, time_in_years=2)

將發生以下錯誤:

——————————————————————————————————————-TypeError Traceback (most recent call last) in ——> 1 calculate_compound_interest(principal=1000, rate=5, time_in_years=2)TypeError: calculate_compound_interest() got an unexpected keyword argument ‘principal’——————————————————————————————————————-TypeError Traceback (most recent call last) in ——> 1 calculate_compound_interest(1000, 5, time_in_years=2)TypeError: calculate_compound_interest() got an unexpected keyword argument ‘time_in_years’

因為我們沒有考慮呼叫方顯式地使用位置引數,所以程式碼中斷。

python3。8中引入了一個解決方案,我們可以使用/引數重新定義函式,該引數指示僅位置引數的結束位置。程式碼如下:

# 複利計算器年/月複利def calculate_compound_interest(p, r, t_in_y, /, *, # 改變 compounded_monthly=False, to_string=False):

現在這樣呼叫函式就會產生正確的結果:

calculate_compound_interest(100, 5, 2, compounded_monthly=True)# 10。49413355583269calculate_compound_interest(100, 5, 2, to_string=True)# ‘$10。25’

但是,如果我們這樣呼叫:

calculate_compound_interest(p=1000, r=5, t_in_y=2)

也會顯示相應的錯誤:

——————————————————————————————————————-TypeError Traceback (most recent call last) in ——> 1 calculate_compound_interest(p=1000, r=5, t_in_y=2) 2TypeError: calculate_compound_interest() got some positional-only arguments passed as keyword arguments: ‘p, r, t_in_y’

以上。

以上為(未艾資訊www。weainfo。net)網站機器翻譯內容,如果有表述錯誤,敬請指正。 來源:https://betterprogramming。pub/5-common-anti-patterns-in-python-a9d6443fabe4,作者:Sayar Banerjee

頂部