前回の続き。
PEP 562 -- Module __getattr__ and __dir__を試してみた - Qiita
「__getattr__と__dir__関数を試してみた」と言いながら、__dir__を試していないことに気づいた。
前回の続きでmain3.pyを追加して実行すると以下のようになる。
getattr/main3.py
import getattr
print(dir(getattr))
実行結果
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__getattr__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'importlib']
ここには、file_loaderやdb_loaderが追加されていない。
そこで、__init__.pyに__dir__()を追加する。
__init__.py
import importlib
__all__ = ['file_loader', 'db_loader']
def __getattr__(name):
if name in __all__:
return importlib.import_module("." + name, __name__)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def __dir__():
return __all__
実行結果
['db_loader', 'file_loader']
ただし、これだと__all__に代入した2つしか出力されなくなるので、
__init__.py
import importlib
import sys
__all__ = ['file_loader', 'db_loader']
def __getattr__(name):
if name in __all__:
return importlib.import_module("." + name, __name__)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
# 位置が重要
__all__ += dir(sys.modules[__name__])
def __dir__():
return __all__
実行結果
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__getattr__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'db_loader', 'file_loader', 'importlib', 'sys']
こうすることで、元のアトリビュートに追加することができる。
注:__dir__()の中でdir()を呼ぶと無限再起してしまいます
ちなみに、あまり考えずにgetattrディレクトリを作ってパッケージ名にしてしまったけど、getattr()組み込み関数があるのでよくない命名だった。