はじめに
以前、TypedDictを利用した「Options Objectパターン」の手法について
ふれていたが、使いどころが模索できていないままでした。
今回はコードを書く中で見つけた使いどころについて紹介します。
ラッパー関数作成時での使いどころ
下記は「pywinauto」を利用し、指定要素の子に位置する
ボタン要素を取得する処理です。
parent_window_specification_object.child_window(
class_name="Button", title="保存"
)
この処理をラップする際には、
引数で親要素オブジェクトと検索条件の2種類を渡す必要がありますが
必ず渡す親要素オブジェクトと、子要素の検索条件を分離した方が読みやすくなります。
その点を考慮し、TypedDictを利用して
引数を役割をごとに分離した以下のようなラッパー関数を作成してみました。
from typing import Any, NotRequired, TypedDict
from pywinauto import WindowSpecification
# child_window()に渡す引数の管理
ChildSearchCriterias = TypedDict(
"ChildSearchCriterias",
{"class_name": NotRequired[str], "title": NotRequired[str], "found_index": NotRequired[int]},
)
def child_window_wrapper(
parent_element: WindowSpecification, child_search_criterias: ChildSearchCriterias
) -> WindowSpecification:
"""child_windowメソッドのラッパー関数
Args:
parent_element (WindowSpecification): 親要素のオブジェクト
child_search_criterias (ChildSearchCriterias): 子要素の検索条件
Returns:
WindowSpecification: 探索結果のオブジェクト
"""
criterias: dict[str, Any] = {}
if child_search_criterias.get("class_name"):
criterias["class_name"] = child_search_criterias["class_name"]
if child_search_criterias.get("title"):
criterias["title"] = child_search_criterias["title"]
# 0以上の数値型のみを引数とする
index_value: int | None = child_search_criterias.get("found_index")
if isinstance(index_value, int) and index_value >= 0:
criterias["found_index"] = index_value
return parent_element.child_window(**criterias)
このように設計する利点としては、ラッパー関数を呼び出す際の渡す引数が
(必須要素, {オプション要素})の形式となり、可読性が向上しています。
ラッパー関数を呼び出す際は、以下のようになり役割ごとで分離でき、
メソッド引数についても、引数名指定と同じように利用ができるのも利点です。
# ラップせずに利用(比較用)
parent_window_specification_object.child_window(
class_name="Button", title="保存"
)
# 自作ラッパー関数を利用
child_window_wrapper(
parent_window_specification_object,
{"class_name": "Button", "title": "保存"},
)
補足として、ラッパー関数の引数にDict型で渡すので、
そのままキーワード可変長引数(**kwargs)で、ラッピング対象へ渡すことも可能です。
しかし、その場合だと誤った引数名を指定して渡してしまう可能性が考えられます。
その部分についても、TypedDictを使うことで予防しています。
あとがき
Pythonにおける型定義関連の部分はバージョンが上がるごとに充実してきており
個人的には注目している箇所の1つであり、どんどん使いたい派です。
今回はpywinautoを一例として出したが、
他にもselenium、playwrightのラッパー関数を作成する場合や
Requestsのように引数が多数あるメソッドをラップする際にも
活用できると考えています。
参考
- pywinauto.application module — pywinauto 0.6.8 documentation