🐍

Python 3.7+ で typed dynamic property みたいなことしたいメモ

2023/03/09に公開

背景

class のインスタンス作ったときには property は作らない.
ユーザーが dynamic に(実行時に)つくったときに実行時型チェックしたい.

c = MyClass()
print(c.name) # NG. name はまだ存在しない
c.name = "" # str. OK
c.name = 3 # Error!
  • 型付き property がほしい(property の名前と型は事前にわかっているようなのがある)
  • None 状態はつくらず, property が無い状態を作りたい
  • もしくは None には別の意味をもたせたい

のようなとき.

実装案

Python 3.8 だと TypedDict があり, より型の保証ができるとは思いますが,
まずは 3.7 で考えます.
↓の簡単な例であれば 3.5 あたりでも動くかもしれません.

class MyClass:
  _builtin_props = { 'name': str, 'distance': int }

  def __init__(self):
      pass
      
  def __setattr__(self, name, value):
      if name in MyClass._builtin_props:
          ty = self._builtin_props[name]
	  
          if not isinstance(value, ty):
	      if not isinstance(value, ty):
                  raise TypeError(
                      "Built-in property `{}` is type of `{}`, but got type `{}` for the value.".format(
                          name, ty.__name__, type(value).__name__))

          self.__dict__[name] = value

__getattr__, __getitem__ (myval["my:key"] のように [] アクセス(literal では記述できない文字が含まれるときなど))は適宜必要に応じて実装して.

注意点

__getattr__ も定義していて, __getattr__ 内で, 例えば↑で class variable(property) の _builtin_props のスペルミスしてしまうなどで存在しない property にアクセスしようとすると, 無限に再帰で __getattr__ が呼ばれてしまい実行時エラーになるので気をつけましょう. 😢

TODO

  • numpy や torch を見るとより良い方法があるかもしれません(dtype で型チェックとかしていたはず)

Discussion