# Python的一些套件(black、isort、pylint) + pre-commit [TOC] ### 遇到的問題 - 在寫python的時候時常都會有這些問題 1. 這個function輸入/出參數那麼多,我該寫成一行讓整體程式碼比較短,還是換行讓參數更簡潔哩? 2. 我import了一堆東西,有來自內建的、第三方的、本地的,到底怎麼排比較好啊? 3. 我命名的參數、變數會不會有什麼問題啊? ### 解決方案 1. 到底什麼時候要換行,你搞得我好亂啊!!! - 使用`formatter`相關套件來解決這類的問題,像是`換行位置`、`注釋的格式`、`注釋掉的程式碼`或`紀錄檔格式中的錯誤做法之類`的問題 - formatter御三家 - black (⭐️ 30.4k) - yapf (⭐️ 12.9k) - autopep8 (⭐️ 4.2k) - [black使用教學](https://sean22492249.medium.com/%E4%BD%BF%E7%94%A8-black-formatter-%E5%9C%A8-vscode-4217b8ceb6f2) - Example - 原本 ```python # 1 def discard_card(self, chosen_player: "Player" = None, discarded_card: Card = None, with_card: "Card" = None): pass # 2 def handle_card_action(self, turn_player: "Player", discarded_card: "Card", action: Union[GuessCard, ToSomeoneCard, None]) -> None: pass # 3 def to_dict(self): return dict(game_id=self.id, players=[x.to_dict() for x in self.players], rounds=[x.to_dict() for x in self.rounds]) ``` - `black xxx.py`後 ```python # 1 def discard_card( self, chosen_player: "Player" = None, discarded_card: Card = None, with_card: "Card" = None, ): pass # 2 def handle_card_action( self, turn_player: "Player", discarded_card: "Card", action: Union[GuessCard, ToSomeoneCard, None], ) -> None: pass # 3 def to_dict(self): return dict( game_id=self.id, players=[x.to_dict() for x in self.players], rounds=[x.to_dict() for x in self.rounds], ) ``` 2. 解決一堆import問題的套件 - 使用`isort`套件來解決這類的問題,將import分為「內建」、「第三方」、「本地」套件排序 - [isort使用教學](https://blog.kyomind.tw/isort/) - Example - 原本 ```python # 本地 from love_letter.models.cards import Card, Deck, PriestCard, find_card_by_name from love_letter.models.exceptions import GameException from love_letter.web.dto import GuessCard, ToSomeoneCard # 第三方 from copy import deepcopy from dataclasses import dataclass from typing import List, Union # 內建 import secrets ``` - `isort xx.py`後 ```python # 內建 import secrets # 第三方 from copy import deepcopy from dataclasses import dataclass from typing import List, Union # 本地 from love_letter.models.cards import Card, Deck, PriestCard, find_card_by_name from love_letter.models.exceptions import GameException from love_letter.web.dto import GuessCard, ToSomeoneCard ``` 3. 命名的參數、變數有什麼問題啊? - 使用`Linter`套件來解決這類的問題,檢查你的`code`,告訴你哪裡寫的不好,應該要改進。強大的PEP8判斷,讓你快速學習coding style - Linter有以下兩款 - [flake8(⭐️ 2.5k)](https://github.com/PyCQA/flake8) - [pylint(⭐️ 4.4k)](https://github.com/PyCQA/pylint) - pycharm有plugin可以使用,可以在`.pylintrc`中的`disable`設定要關閉的格式驗證(`missing-module-docstring`、`missing-class-docstring`) - [Pycharm中pylint使用教學](https://cloud.tencent.com/developer/article/1486578) - Example - 原本 ```python def decorate_with_card_usage(self, raw_result): for round in raw_result['rounds']: pass ``` - `pylint xx.py`後,跟你說你重複定義了內建的方法`round`,換一個命名就能解決此問題 ```sh xx.py:2:8: W0622: Redefining built-in 'round' (redefined-builtin) xx.py:2:8: W0612: Unused variable 'round' (unused-variable) ``` ### 上面介紹了一堆套件,拿到我每次commit、push都要自己每個套件都跑過一次嗎? 應該有個東西可以幫忙整理吧? - `pre-commit`: Git pre-commit hooks 工具,能夠在執行`git commit`時幫你進行你設定的檢查,必須另外寫`.pre-commit-config.yaml`來指定上面說的三種套件 - `.pre-commit-coifig.yaml` ```yaml repos: - repo: https://github.com/psf/black rev: 22.10.0 hooks: - id: black name: black - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: - id: isort name: isort - repo: https://github.com/PyCQA/pylint rev: v2.15.5 hooks: - id: pylint name: pylint entry: pylint language: system types: [python] args: [ "-rn", # Only display messages "-sn", # Don't display the score ] ``` - [pre-commit使用教學](https://myapollo.com.tw/zh-tw/pre-commit-the-best-friend-before-commit/) ### 總結 - 透過這些套件讓我們在開發python project的時候可以更加安心的開發,不用擔心`排版`、`語法錯誤`、`不符合PEP8`等事情。最後再透過`pre-commit`來幫我們統一整合在`git commit`時觸發,讓我們push上去的code都是經過這些套件驗證過的。 - 後續:可以在github action加入這些套件的CI測試,防止有人沒按照規矩來push