kb84tkhrのブログ

何を書こうか考え中です あ、あと組織とは関係ないってやつです 個人的なやつ

Pythonのモジュールとパッケージがわからない

pytestを試している途中、テスト用のモジュールをtestsフォルダに入れただけで
よくわからなくなったのでモジュールとパッケージについて調べる

公式を見ながらいくよ
チュートリアルから:6. Modules — Python 3.7.3 documentation
リファレンスから:5. The import system — Python 3.7.3 documentation

そもそもモジュールとかパッケージとかいうのはなんなのか

A module is a file containing Python definitions and statements.

モジュールはPythonの定義や文を書いたファイル

Packages are a way of structuring Python’s module namespace by using “dotted module names”.

パッケージはドットを使ってモジュールの名前空間を構成するもの

A regular package is typically implemented as a directory containing an __init__.py file.

通常のパッケージは__init__.pyが含まれたディレクトリのこと

__init__.pyとは何か

The __init__.py files are required to make Python treat directories containing the file as packages.

パッケージであることを示す目印

In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.

空っぽでもいいし、初期化のコードを書いてもいい

これくらいで
まずは普通に上位のフォルダから下位のフォルダを参照する場合からやってみる

+- pymod/
  +- mainmod.py
  +- sub1
    +- __init__.py
    +- submod1.py

mainmod.pyはこうして

import sub1.submod1-1

print(sub1.submod1.submod1_func())

submod1-1.pyをこうした

__init__.pyはからっぽ

$ python mainmod1.py
submod1_func

できた

__init__.py(と__init__.pyc)を消してやってみる

$ python mainmod1.py
Traceback (most recent call last):
  File "mainmod1.py", line 1, in <module>
    import sub1.submod1_1
ImportError: No module named sub1.submod1_1

パッケージとして認識されなくなった
想定通り

mainmod1.pyをこう書き換えても大丈夫

from sub1 import submod1_1

print(submod1_1.submod1_1_func())

しかしsubmod1_1とわざわざ指定するのは面倒
__init__.pysubmod1_1の名前をまるごとimportしてやると
submod1_1の指定を省略できる

import sub1

print(sub1.submod1_1_func())

flake8が
'from submod1_1 import *' used; unable to detect undefined namesとか
'submod1_1.*' imported but unusedとか言ってくるのはちょっと気になる
ほかに推奨の書き方があるのか
それともはじめから__init__.pyに書くものなのか

ためしに/opt/python3.7.3/lib/python3.7/unittest__init__.py
見てみると、*ではないけどモジュールから名前をimportしてた
flake8をかけてみる

$ flake8 __init__.py
__init__.py:58:1: E402 module level import not at top of file
__init__.py:59:1: E402 module level import not at top of file
__init__.py:61:1: E402 module level import not at top of file
__init__.py:61:1: F401 '.suite.BaseTestSuite' imported but unused
__init__.py:62:1: E402 module level import not at top of file
__init__.py:62:1: F401 '.loader.makeSuite' imported but unused
__init__.py:62:1: F401 '.loader.getTestCaseNames' imported but unused
__init__.py:62:1: F401 '.loader.findTestCases' imported but unused
__init__.py:62:80: E501 line too long (80 > 79 characters)
__init__.py:64:1: E402 module level import not at top of file
__init__.py:64:1: F401 '.main.TestProgram' imported but unused
__init__.py:65:1: E402 module level import not at top of file
__init__.py:66:1: E402 module level import not at top of file
__init__.py:66:80: E501 line too long (80 > 79 characters)
__init__.py:74:1: E302 expected 2 blank lines, found 1

unusedも出てるし気にしなくていいのかな
でも気になる
かといってまるごと無視もしたくないし

次いく

兄弟フォルダを参照する場合

When packages are structured into subpackages (as with the sound package in the example), you can use absolute imports to refer to submodules of siblings packages.

パッケージがサブパッケージの形になってれば兄弟パッケージは「絶対import」ができる

sub2フォルダを作ってsubmod2_1.pyを入れる
こう書けるってことだろうか

import pymod.sub1

print(pymod.sub1.submod1_1_func())
$ python submod2_1.py
Traceback (most recent call last):
  File "submod2_1.py", line 1, in <module>
    import pymod.sub1
ImportError: No module named pymod.sub1

pymodフォルダに__init__.pyがないから?
いや作ってみたけどダメ
sub2フォルダにも作ってみたけどダメ

import pymod.sub1from pymod import sub1とか
from pymod.sub1 import submod1_1とかに変えてみてもダメ

子フォルダで実行してるのがだめかと思って
mainmod1_1.pyimport sub2.submod2_1を追加して
pymodフォルダで実行してもやっぱりpymodなんて知らないと言う
そもそも今いるフォルダがpymodパッケージだと思ってないのかな?

相対パスのところだけどこう書いてあるのが気になる

Since the name of the main module is always "main", modules intended for use as the main module of a Python application must always use absolute imports.

メインモジュールの名前はいつも"main"なので、Pythonアプリケーションのメインモジュールは必ず絶対インポートを使え

pymodじゃなくて__main__と思ってるのかなあ
python submod2_1.pyで実行したときはsubmod2_1.pyがメインモジュールって
ことになってうまくいかない、という話はありそうだけど
じゃあどう書けばいいの

なにか勘違いしている気がするけどわからない
明日もう一度ちゃんと読んでみよう