# 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