f
が関数かどうか判定するときにcallable(f)
使ってませんか?
同じ型判定なのに、文字列だとisinstance(x, str)
だし、書き方が統一的ではないのが気になります。
typingを使おう
Pythonは近年は型注釈の整備が非常に進んできています。それに伴い、型にかかわるモジュールも充実してきています。
-
typing
... 基本的な型が入っている。たいていはこれで事足りる。 -
types
... 細かな型まで入っている。 -
typing_extensions
... 開発途中だが「こんなのアリ!?」みたいな型まで入っている。
これらを使いこなすことで、高度なプログラムだけでなく、読みやすくデバッグしやすいコードが書けるようになります。加えて、VSCodeではifブロック内で予測変換が利くようになります。
以下、具体例を見ていきましょう。
1. 関数かどうかを判定
def f(x):
print(x)
これを判定するには従来は
callable(f) # True
でしたが、Callable
型が使えます。
from typing import Callable
isinstance(f, Callable) # True
2. イテレータの判定
for文に使う前にイテレータとして使えるかどうかを判定するときなど、従来は
if hasattr(x, "__iter__"):
# __iter__メソッドの有無で判定
for i in x:
...
とか
def is_iterable(x):
# 判定する関数を自作
try:
iter(x)
except TypeError:
out = False
else:
out = True
return out
とか書く必要がありましたが、Iterable
型が使えます。
from typing import Iterable
isinstance([1, 2, 3], Iterable) # True
isinstance(1, Iterable) # False
3. ジェネレータの判定
何かの関数で
out = f()
と返されたときに、out
が値を持っているのか、ジェネレータなのでこれから値を取り出していかなければならないのか判定するときがあります。inspect
モジュールを使って
import inspect
inspect.isgenerator(out)
でもよいですが、types
モジュールにあるGeneratorType
が使えます。
from types import GeneratorType
isinstance(out, GeneratorType)
4. 特定のメソッドのセットを持っているか判定
hasattr
を連発せざるを得ない状況がたまにありますが、とても読みにくくなります。
このときに大活躍するのがtyping_extensions
のProtocol
型です。Protocol
型は例えば、fit
とpredict
をメソッドとして持つクラスといったような「クラスのひな型」を定義でき、isinstance
と組み合わせられます。
from typing_extensions import Protocol, runtime_checkable
@runtime_checkable # isinstanceで使うにはこれが必要
class ScikitLearnProtocol(Protocol):
fit: Callable
predict: Callable
scikit-learn
は大抵こういうクラス構造になっているので、True
と判定されます。
from sklearn.linear_model import Lasso
model = Lasso()
isinstance(model, ScikitLearnProtocol) # True
isinstance(1, ScikitLearnProtocol) # False
おわりに
ほかにも、標準の関数を判定するBuiltinFunctionType
などもあります。
また、個人的に注目しているのがTypedDict
で、まだisinstance
には対応していないのですが、辞書のキーの名前と値の型を対応させることができるので、将来の進展が楽しみです。
それでは、良いコーディングライフを!