Python
python3

PEP 562 -- Module __getattr__ and __dir__を試してみた Part2

前回の続き。

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()組み込み関数があるのでよくない命名だった。