昨天介紹了 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
寫測試。
最後一樣感謝大家耐心地看完這篇文章,有任何建議或問題都可以在留言告訴我,明天見,掰掰。