21世紀も20年経とうとしている今、Python スクリプトを走らせてスペルミスなどの単純ミスて止まるのは文明的じゃない; flake8 などの静的解析を使おう。それは多くの記事があるのでいいとして、自作の package に flake8 を使うと
F401 'mypackage.module1' imported but unused
みたいなことをいう。その import には意味があるんだけど... という話。
設定
package とは複数のモジュールがひとつのディレクトリに入ったもの:
mypackage/
+ __init__.py
+ module1.py
+ module2.py
import mypackage
して使う。
module1.py
では関数 function1()
が、module2 には class Class2
が定義してあるとしよう。
問題
import mypackage
mypackage.module1.function1()
c = mypackage.Class2()
のように使うために __init__.py
に
import mypackage.module1
from mypackage.module2 import Class2
と書きたい。すると、
$ flake8 mypackage/*.py
mypackage/__init__.py:1:1: F401 'mypackage.module1' imported but unused
mypackage/__init__.py:2:1: F401 'mypackage.module2.Class2' imported but unused
と言われる。確かに __init__.py
では使ってないけど意味はあるんだ。これらの import 文が無いと
import mypackage.module1
from mypackage.module2 import Class2
と毎回書かなければならないので、それはナイ。PEP8的な正解は?
解決法1(手っ取り早い方法)
# noqa
とコメントすると flake8 などの linter が無視する。
from . import module1 # noqa
from .module2 import Class2 #noqa
解決法2
やらなくても動くけど、package の公式の機能は __all__
にリストアップべき。
Any backwards compatibility guarantees apply only to public interfaces. Accordingly, it is important that users be able to clearly distinguish between public and internal interfaces.
To better support introspection, modules should explicitly declare the names in their public API using the
__all__
attribute.
すなわち、
from . import module1
from .module2 import Class2
__all__ = ['module1', 'Class2']
まず、.
を使えば「このパッケージ」を意味するので、mypackage
と繰り返し書かなくて良い。パッケージ名を変えても修正しなくていいという利点もある。そして __all__
というリストに import したものを書く。すると、unused でなくなったので flake8 さんは満足。
__all__
とは
flake8 を満足させるためだけのダミーの変数というわけではなく、一応意味はある。
__init__.py
に import を書かず、
__all__ = ['module1']
と書くと、奨励されないアイツ from mypackage import *
した時に
from mypackage import *
module1.funtion()
module1
が読み込まれる。もし、__all__
も from . import module1
も無ければ、import *
でも module1
は読み込まれない。
from . import module1
があれば __all__ = ['module1']
は不要なので __all__
は重複とは言える。逆に、__all__
だけでは import mypackage
の時には自動 import してくれない。とはいえ、PEP8 に書かれていることなので、他の人も使う package として公開するときはちゃんと __all__
を書くのが良さそう。
参考
すべてはここに書いてあった。