五個良好的Python編程習慣,助你成為Python高手!
今天筆者將向大家分享5個良好的Python編程習慣,大牛認證,通過不斷實踐,助你寫出更Pythonic的代碼,讓你向Python大師之路更進一步。
1. 常用if __name__ == '__main__'
假設我們在 module.py 中寫了一個模擬連接到數據庫的函數,并調用它:
import time
def sim_conn_to_db() -> None:
print('Connecting to database...')
time.sleep(3)
print('Connected to database successfully!')
sim_conn_to_db()
輸出:
Connecting to database...
Connected to database successfully!
現在,假設我們保持 module.py 的代碼不變,然后在 main.py 中調用 sim_conn_to_db() 方法:
from module import sim_conn_to_db
sim_conn_to_db()
輸出:
Connecting to database...
Connected to database successfully!
Connecting to database...
Connected to database successfully!
wow,你發現了么?sim_conn_to_db() 方法執行了兩遍。這是為什么呢?原因在于我們通過 from module import sim_conn_to_db 語句引入 sim_conn_to_db() 方法時,Python解釋器會加載整個 module.py 腳本,逐行加載代碼。因此,在加載的時候已經執行了一次 sim_conn_to_db() 方法;然后,在 main.py 腳本中我們又再一次調用了 sim_conn_to_db() 方法,所以,該方法被執行了2次。
那么改如何避免這種情況呢?這就是今天我向大家分享的第1個好習慣,總是使用 if __name__ == '__main__' 語句執行檢查。比如,在 module.py 文件中加入改語句:
import time
def sim_conn_to_db() -> None:
print('Connecting to database...')
time.sleep(3)
print('Connected to database successfully!')
if __name__ == '__main__':
sim_conn_to_db()
然后,我們再次執行 main.py 文件,sim_conn_to_db() 方法就不會被執行2次。因為 if __name__ == '__main__' 語句確保了只有直接執行 module.py 文件的時候,sim_conn_to_db() 方法才會被調用,在別的 .py 文件中調用該方法時只會執行一次。
如果你使用的Pycharm IDE的話,if __name__ == '__main__' 的另一個好處就是,在行號處會出現一個綠色的運行按鈕,單擊該按鈕你可以直接執行當前腳本文件或其他操作.
2.main函數讓代碼更有組織性
假設我們有以下代碼片段:
def greeting(name: str) -> None:
print(f"Hello {name}!")
def bye() -> None:
print("Bye! See you soon!")
if __name__ == '__main__':
greeting(name='Jack')
bye()
這里,我們定義了兩個簡單的問候和告別方法,并且調用它們。完全沒有問題,正確實現了預期功能。但是,如果有很多個函數呢?也在 if __name__ == '__main__': 語句中調用的話就顯得臃腫且復雜,毫無組織性。因為 if __name__ == '__main__': 語句是函數的執行入口,我們應該盡量保證簡潔。
這個時候就用到了今天我給大家分享的第2個好習慣,定義一個 main 函數,將所有函數調用和其他邏輯都放到該函數中,然后在入口處只需要調用 main 函數即可。
def greeting(name: str) -> None:
print(f"Hello {name}!")
def bye() -> None:
print("Bye! See you soon!")
def main() -> None:
greeting(name='Jack')
bye()
if __name__ == '__main__':
main()
這樣,即使后續有新增的方法調用,只需要在main函數中添加即可,不僅確保了腳本運行入口處語句的簡潔,同時讓整個代碼結構更具組織性。另一方面,也讓你的代碼更具可讀性。
Tips:你可以將 main 函數當做是你的管家,無論多復雜、數量眾多的事務都交給管家一個人去統籌安排,而你只需要掌控管家一個人即可。
3. 保持函數的單一性和簡潔性
假設我們有一個根據姓名、年齡和身份證來判斷某人是否能夠加入俱樂部的方法,如下:
def enter_club(name: str, age: int, has_id: bool) -> None:
if name.lower() == 'stefan':
print('Get out of here Stefan, we don\'t want to trouble.')
if age >= 18 and has_id:
print('You may enter the job.')
else:
print('Sorry, you may not enter the club.')
def main() -> None:
enter_club(name='Stefan', age=18, has_id=True)
enter_club(name='Bob', age=29, has_id=True)
enter_club(name='Ellena', age=20, has_id=False)
enter_club(name='Bony', age=21, has_id=True)
if __name__ == '__main__':
main()
很明顯,上面的代碼正確實現了我們想要的功能。但我并不推薦這種做法,因為 enter_club 方法中涉及了很復雜的邏輯,并不符合Python函數編程的單一職責原則。
因此,這里就會用到今天我給大家分享的第3個好習慣——盡可能保持函數的單一性和簡潔性。enter_club 方法中同時涉及到姓名、年齡和身份的判斷,實際上我們可以將其拆分成多個具有單一職責的函數。
def is_black_list(name: str) -> bool:
return name.lower() == 'stefan'
def is_adult(age: int, has_id: bool) -> bool:
return age >= 18 and has_id
def enter_club(name: str, age: int, has_id: bool) -> None:
if is_black_list(name):
print('Get out of here Stefan, we don\'t want to trouble.')
if is_adult(age, has_id):
print('You may enter the job.')
else:
print('Sorry, you may not enter the club.')
def main() -> None:
enter_club(name='Stefan', age=18, has_id=True)
enter_club(name='Bob', age=29, has_id=True)
enter_club(name='Ellena', age=20, has_id=False)
enter_club(name='Bony', age=21, has_id=True)
if __name__ == '__main__':
main()
這里,我們將對姓名和年齡、身份的判斷拆分為兩個獨立的方法 is_black_list 和 is_adult。拆分后的函數職責單一,邏輯簡單清晰。確保實現相同功能的同時,增強了代碼的可讀性、可維護性以及可擴展性。
4. 盡可能多用類型提示
第4個好習慣就是類型注釋(type hints),如果你還不知道什么是類型注釋,以及它的諸多好處,你可以閱讀我的上一篇文章“Python類型提示(type hints):提升代碼質量與可讀性的利器!”。
這里,我們只是舉幾個簡單的例子來說明類型注釋的實用之處。
4.1 降低代碼潛在bug或錯誤的風險
比如,以下代碼段:
def add(a, b):
return a + b
if __name__ == '__main__':
result = add(a=1, b=2)
print(result) # 3
正確實現了整數的加法功能。但是,由于參數 a 和 b 并沒有任何類型說明,如果你的用戶或同事在調用 add 方法時,給了錯誤的參數類型,很可能造成意料之外的結果甚至程序錯誤,比如,
def add(a, b):
return a + b
if __name__ == '__main__':
result = add(a='1', b=2)
print(result)
只要給參數 a傳遞字符串值,都會導致 TypeError,因為字符串和整數不支持連接操作。類似地,如果給參數 b 傳遞字符串值,同樣會得到 TypeError,因為整數和字符串不支持加法操作。
然而,如果給參數 a 和 b 都傳遞字符串值,會發生什么呢:
if __name__ == '__main__':
result = add(a='1', b='2')
print(result) # 12
雖然程序可以正常執行,但是得到的結果是12,這是字符串連接結果,而不是我們想要的整數相加結果。
解決這種潛在錯誤或不期望結果的方法就是在方法定義中使用類型注釋:
def add(a: int, b: int) -> int:
return a + b
這樣,在方法調用時,如果給參數傳遞了不符合定義時所給的參數類型,編譯器會提示:
有了這個提示,我們就知道應該給參數傳遞的是整數類型,而不是字符串。有效降低了潛在bug或錯誤發生的幾率,特別是在大型項目中。因為解決bug永遠都是一件令人十分惱火的事??????。
4.2 提高編碼效率
在很多IDE中,都提供了根據上下文進行代碼補齊的功能,比如Pycharm。但是,如果不知道變量類型的前提下,編譯器是沒辦法給你任何推薦的。比如:
這里,我想對一個DataFrame進行一系列的處理,由于不知道變量的數據類型,在輸入 . 之后,編譯器無法給出有效的推薦。
當我們增加類型注釋后:
這時,編譯器就會將DataFrame具有的所有方法和屬性全都列出來供我們選擇,可以快速找到我們想要的方法或屬性,從而提高編碼效率。
關于類型注釋的更多用法,你可以閱讀我的上一篇文章“Python類型提示(type hints):提升代碼質量與可讀性的利器!”。
5. 善用列表推導式
假設有一個不同年齡構成的列表,我們想要篩選出所有大歲數(年齡≥30)的人并存儲在新的列表中:
ages: list[int] = [18, 16, 20, 35, 40, 53, 65, 32, 80, 96]
olders: list[int] = list()
for age in ages:
if age >= 30:
olders.append(age)
print(f'People with older age: {olders}')
# Output: People with older age: [35, 40, 53, 65, 32, 80, 96]
這里我們通過循環實現了想要的功能。但其實,還有另一種更簡潔的方法,就是今天我給大家分享的最后一個好習慣——善用列表推導式:
ages: list[int] = [18, 16, 20, 35, 40, 53, 65, 32, 80, 96]
olders: list[int] = [age for age in ages if age >= 30]
print(f'People with older age: {olders}')
完整實現相同功能的同時,一下子將4行的核心代碼編程1行,是不是既簡潔又高效呢?
6. 結論
感謝你的閱讀,希望本文簡潔、清晰的內容能夠對你有所幫助!See you next time!