你在濫用Python嗎?初學者常會遇到的5個情景
本文轉(zhuǎn)載自公眾號“讀芯術(shù)”(ID:AI_Discovery)。
Python擁有眾多學習者,可謂是如今許多編程初學者的首選語言。易學的語法、豐富的庫和大量的社區(qū)都是Python飛速發(fā)展的主要原因。
6年前,在我掌握了一系列Java之后接觸到Python時,經(jīng)常發(fā)現(xiàn)自己在寫Python代碼時腦子里還想著Java。作為一個新手,我沒有充分利用Python的優(yōu)點,甚至在某些情況下,我濫用了它。
現(xiàn)在,我仍然看到一些初學者在沒有先花時間閱讀最佳實踐和建議的情況下就開始用Python寫代碼。為解決這個問題,我列出了下面5個濫用Python的情景,并給出了相關(guān)改進建議。
1. 使用列表時
列表允許存儲各種數(shù)據(jù)類型的元素且不限制大小,盡管這種靈活性使列表成為收集數(shù)據(jù)的首選,但實際上仍有一些使用和不使用它的最佳實踐情景。
在存儲具有相同性質(zhì)(數(shù)據(jù)類型和含義)的元素時,應(yīng)該使用列表。Python不會通過編程來限制這一點,在列表中存儲單個自然項讓開發(fā)人員的工作更輕松。開發(fā)人員很容易預(yù)測將來列表會有哪些項,并確信地編寫腳本。
思考下面的物品列表。這個列表并不包含單一性質(zhì)的項目,開發(fā)人員無法確定該列表是否包含房屋部件、尺寸或其他東西,因此他應(yīng)該分別處理不同的項目:
- list_of_things = ['Door', 2,'Window', True, [2.3, 1.4])]
思考下面的水果列表和分數(shù)列表。從前兩個項目中,你很容易推斷出第一個列表會始終包含水果名字,而第二個列表始終包含分數(shù)值:
- list_of_fruits = ['apple','orange', 'pear', 'cherry', 'banana']
- list_of_scores = [80, 98, 50, 55, 100]
在存儲具有不同含義或數(shù)據(jù)類型的項目時,使用元組更合適。元組不具備在不創(chuàng)建新對象的情況下,提供存儲不受限項目的靈活性(因為元組是不可變的)。
2. 迭代連接字符串時
在Python中所有東西都是對象,包括可變和不可變對象。每當更新分配給對象的值時,不可變對象需創(chuàng)建新對象,而可變對象則不需要。
假設(shè)你想在一個字符串中生成整個字母表。因為字符串是不可變對象,所以每當使用“+”運算符連接字符串值時,就會得到一個新的對象。
- one_line_alphabet = ''
- for letter_index in range(ord('a'), ord('z')):
- one_line_alphabet +=chr(letter_index)
Join函數(shù)是連接字符串的首選方法。使用join函數(shù)可將計算時間縮短約3倍。在我做的一項測試中,迭代連接100萬個字符串值耗時0.135秒,若使用join( )函數(shù)則只需0.044秒。
- small_letters = [chr(i) for i inrange(ord('a'), ord('z')+1)]
- single_line_alphabet = ''.join(small_letters)
因此,需要連接字符串列表時請使用join函數(shù)。若使用join函數(shù)連接幾個字符串,這并不會直觀感受到性能的差異。若要連接幾個字符串值,請使用.format而不是“+”運算符。例如:
- name = 'John'
- surname = 'Doe'
- full_name = '{name} {surname}'.format(namename=name, surnamesurname=surname)
3. 讀寫文件時
若要使用Python讀寫文件,首先需要用內(nèi)置的open函數(shù)打開文件。打開文件,讀取或?qū)懭雰?nèi)容以及關(guān)閉文件。進行操作時,可能會出現(xiàn)一些問題,比如忘記關(guān)閉文件和異常處理失敗。
操作完成后,若忘記關(guān)閉文件會導(dǎo)致后續(xù)問題。比如,如果在寫入文件后忘記關(guān)閉該文件,那么寫入操作將不會保存至文件中,并且在文件仍然保持打開狀態(tài)時,你將保留在計算機中分配的資源。如果在處理文件時,沒有手動處理異常和錯誤,那么文件將保持打開的狀態(tài)。
- f = open(file='file.txt', mode='r')
- lines = f.readlines()
- ...
- f.close()
建議在打開文件時使用with關(guān)鍵字。with是一個上下文管理器,它能封裝代碼并能確保自動處理異常。比如,當你讀寫文件時,with-body中可能出現(xiàn)的任何故障,都能自動處理異常,并且始終保持該文件關(guān)閉。
- with open('file.txt') as f:
- read_data = f.read()
- ...
如果跳過with時,你需要自己處理一切,關(guān)閉文件和異常處理都得親自處理。with會讓你的生活更輕松,讓情況得以控制。
4. 跳過生成器時
在許多情景中,你需要生成一個值列表,稍后將在腳本中使用這些值。比如,你需要為前100個數(shù)字生成所有3個數(shù)字的組合。
- combinations = []
- value = 100
- for i in range(value):
- for j in range(value):
- for k in range(value):
- combinations.append((i, j,k))
當執(zhí)行的命令完成時,列表組合將包含1M元組,每個元組有3個整型值。這些值將保存在內(nèi)存中,直到被刪除。使用sys模塊中的getobjectsize函數(shù)檢查對象大小,結(jié)果為8.29MB。
不再使用列表存儲值并將它們?nèi)勘4嬷羶?nèi)存,可以創(chuàng)建一個生成器,每當你使用它時,將生成1個組合。這能減少內(nèi)存消耗并提高運行速度。
- defgenerate_combinations_of_three(value):
- for i in range(value):
- for j in range(value):
- for k in range(value):
- yield (i, j, k)gen =generate_combinations_of_three(100)next(gen) # yields (0, 0, 0)
- next(gen) # yileds (0, 0, 1)
- ...
所以,盡可能多地使用生成器。時刻牢記內(nèi)存容量是有限的,并盡可能優(yōu)化內(nèi)存使用。請使用生成器,特別是在開發(fā)可伸縮的解決方案時。
5. 使用推導(dǎo)式時
有一些程序員,他任何用Python編寫代碼都遵循Python之禪(The Zen of Python)的準則。如果是使用Python的新手,可能會傾向于夸大Python之禪的某些觀點,而在其他方面避重就輕。
這一點在逐漸了解推導(dǎo)式時最容易注意到——你傾向于翻譯推導(dǎo)式中的“每一個”循環(huán)。假設(shè)你有一個三維的數(shù)字矩陣,你很可能會想將其平面化。
- matrix = [[[ 1, 2, 3 ],
- [ 4, 5, 6 ],
- [ 7, 8, 9 ]],
- [[ 10, 20, 30 ],
- [ 40, 50, 60 ],
- [ 70, 80, 90 ]]]
使用列表推導(dǎo)式,平面化過程如下:
- flatten_list = [x for sub_matrix inmatrix for row in sub_matrix for
- x in row]
使用循環(huán),平面化過程如下:
- flatten_list = []
- for sub_matrix in matrix:
- for row in sub_matrix:
- for x in row:
- flatten_list.append(x)
列表式很酷,但可讀的代碼更酷。不要試圖總是讓自己使用列表式,即使這樣做可能需要編寫更少的代碼,也不會損失代碼的可讀性。
圖源:unsplash
不論是否有編程經(jīng)驗,每當嘗試使用一種新的編程語言時,請一定要抽出時間閱讀最佳實踐。每種編程語言都有其獨特之處,所以要確保在適當?shù)膱鼍昂侠淼剡\用它們。
Python致力于幫助程序員更高效便捷地完成工作,我們不能忽視可能對代碼生命期產(chǎn)生負面影響的小決策。請盡可能尋找更好的和最佳的解決方案,這是程序員的工作使命。