1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

callableはもう古い!?typingモジュールを使ったPythonのモダンな型判定法

Posted at

fが関数かどうか判定するときにcallable(f)使ってませんか?
同じ型判定なのに、文字列だとisinstance(x, str)だし、書き方が統一的ではないのが気になります。

typingを使おう

Pythonは近年は型注釈の整備が非常に進んできています。それに伴い、型にかかわるモジュールも充実してきています。

  • typing ... 基本的な型が入っている。たいていはこれで事足りる。
  • types ... 細かな型まで入っている。
  • typing_extensions ... 開発途中だが「こんなのアリ!?」みたいな型まで入っている。

これらを使いこなすことで、高度なプログラムだけでなく、読みやすくデバッグしやすいコードが書けるようになります。加えて、VSCodeではifブロック内で予測変換が利くようになります。

以下、具体例を見ていきましょう。

  1. 関数かどうかを判定
  2. イテレータの判定
  3. ジェネレータの判定
  4. 特定のメソッドのセットを持っているか判定

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_extensionsProtocol型です。Protocol型は例えば、fitpredictをメソッドとして持つクラスといったような「クラスのひな型」を定義でき、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には対応していないのですが、辞書のキーの名前と値の型を対応させることができるので、将来の進展が楽しみです。

それでは、良いコーディングライフを!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?