昨天介紹了 Python Enum 型態,運用在 Utils,在之後我把 Utils 加回 statcast_search 裡使用:
# src/baseball_stats_python/statcast/statcast_search.py
params = {
- "hfSea": season if type(season) == str else "|".join(season),
- "hfGT": game_type if type(game_type) == str else "|".join(game_type),
- "hfMo": month if type(month) == str else "|".join(month),
- "hfTeam": team if type(team) == str else "|".join(team),
- "hfOpponent": opponent if type(opponent) == str else "|".join(opponent)
+ "hfSea": get_season_param_str(season),
+ "hfGT": get_game_type_param_str(game_type),
+ "hfMo": get_month_param_str(month),
+ "hfTeam": get_team_param_str(team),
+ "hfOpponent": get_team_param_str(opponent),
}
但也發現了一些問題,第一個是我想在使用者使用 statcast_search 的時候,也可以讓使用者使用 Enums,例如可以寫成 statcast_search(team=MlbTeam.DODGERS, month=Month.JUNE)。不過我昨天誤會使用 str 來設定讓像是 Month.JUNE 能直接得到 Enum 的 value,但其實不是這樣,他是在轉換成 str 的時候才會直接回傳 value,像是 print(Month.JUNE) 或是 str(Month.JUNE) 跟 f"{Month.JUNE}",如果是直接使用 Month.JUNE 則會回傳 <Month.JUNE: '6'>
class Month(Enum):
JUNE = "6"
def __str__(self):
return self.value
print(Month.JUNE) # display 6
str(Month.JUNE) # display 6
f"{Month.JUNE}" # display 6
Month.JUNE # <Month.JUNE: '6'>
所以這樣我們要用傳入 Month.JUNE 當方式使用 statcast_search 的話,就會需要另外處理 Enum 的型態。第一個要改的地方就是判斷型態的第一行,需要多加 Enum 的檢查:
- if (type(game_type) != str and type(game_type) != list)
+ if (type(game_type) != str and type(game_type) != list and game_type not in GameType)
再來就是昨天寫的 classmethod:has_value,要能處裡 str 跟 Enum 會需要再修改一下:
@classmethod
def has_value(cls, value):
- return value in cls._value2member_map_
+ return str(value) in cls._value2member_map_
這樣不管是 str 或 Enum 的檢查都能處理。
當我們開始新增各種 Enum 的時候,會發現會需要重複宣告 has_value 等 method,可以使用 class 的特性,建立一個共用的 EnumBase,把那些會用到的 methods 一起寫進去,就不用重複寫在每個 Enum 裡面了。後來也因為有 all 這個選項,但 Enum 不能直接使用 join,所以也要另外寫一個 classmethod:
from enum import Enum
class EnumBase(Enum):
def __str__(self):
return self.value
@classmethod
def has_value(cls, value):
return str(value) in cls._value2member_map_
@classmethod
def get_all(cls):
return '|'.join(cls._value2member_map_)
這樣不管是舊的 Enum 或是以後需要新增的 Enum 就可以繼承 EnumBase:
class Month(EnumBase):
MARCH_AND_APRIL = "4"
MAY = "5"
JUNE = "6"
JULY = "7"
AUGUST = "8"
SEPTEMBER_AND_OCTOBER = "9"
跟 statcast_search 一樣,如果想 import Enum 的話,在 enums 這個資料夾也會需要 init.py 才能讓 Python 找到相對應的路徑,另外 __init__.py 裡面也可以加入 import,這樣可以在最一開始就拿到 statcast_search
# __init__.py
from .statcast.statcast_search import statcast_search
這樣的話之後安裝套件完就能直接 from baseball_stats_python import statcast_search,就不需要知道路徑就能直接使用 statcast_search。
另外一個想提的是,當我們的程式碼越來越龐大,也會需要用各種 import,如果遇到檔案在同層的不同資料夾,像是
enums
statcast.py
utils
statcast.py
如果我們想在 utils 裡的 statcast.py 引用 enums 裡的 Enum,就會需要用 .. 先讓路徑回上一層再往下搜尋,import 的寫法會是:
from ..enums.statcast import Month
注意這個 .. 事可以再往上一層(如果有的話),只要再加一個點就是了 ...,如果是一個 . 的話就是當前的資料夾。不過使用這些路徑要很小心,可以在這篇文章可以看到更多詳細介紹:6. 模組 (Module)¶
今天讓我們使用 Enum 更完整,也多介紹 import 的延伸,這樣其實算完成簡單的套件使用了。明天看時間,會介紹如何寫 README 或是要來用 pytest 寫測試。
最後一樣感謝大家耐心地看完這篇文章,有任何建議或問題都可以在留言告訴我,明天見,掰掰。