iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0

Enum 延伸

昨天介紹了 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 能直接得到 Enumvalue,但其實不是這樣,他是在轉換成 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)

再來就是昨天寫的 classmethodhas_value,要能處裡 strEnum 會需要再修改一下:

@classmethod
    def has_value(cls, value):
-        return value in cls._value2member_map_
+        return str(value) in cls._value2member_map_

這樣不管是 strEnum 的檢查都能處理。

EnumBase

當我們開始新增各種 Enum 的時候,會發現會需要重複宣告 has_valuemethod,可以使用 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"

套件 Import

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 寫測試。

最後一樣感謝大家耐心地看完這篇文章,有任何建議或問題都可以在留言告訴我,明天見,掰掰。


上一篇
Day 21 - 使用 Enum 來更完善 Utils
下一篇
Day 23 - 為套件用 pytest 寫測試
系列文
上次介紹的棒球套件很少更新了,那就只好自己寫一個!?31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言