package の作成
こちらの記事を参考にしてパッケージを作成してみた過程でいくつか発見があったためメモします.
パッケージは上記記事と同じように以下のような構成となっています.
myapp/ ← プロジェクトのフォルダ(フォルダ名は何でも良いです)
├ setup.py ← packageの情報を書くファイル
└ myapp/ ← このフォルダ名でimportするようになります
├ __init__.py ← フォルダ内のクラスやメソッドを定義するファイル
└ main.py ← 実際にコードを書くファイル(ファイル名は自由)
__init__.py
に from .main import func1
と書くことで,import myapp
したあとでmyapp.main.func1
をmyapp.func1
と省略して記述できます.
つまり,一般的にPythonパッケージは次のように読み込みますが,
import dir1 # 呼び出し: dir1.module1.func1()
import dir1.module1 # 呼び出し: dir1.module1.func1()
from dir1.dir2 import <module> # 呼び出し: module.func1()
from dir1.dir2.module1 import <function or class> # 呼び出し: func1()
__init__.py
を用いると import dir1
のあとに dir1.func1()
で呼び出せます.
module と package の識別
import したものが package ではなく module である場合もあります.
例えば datetime
のパスを確認すると,datetime.py
という module を読み込んでいます.
また,myapp
は myapp/myapp
のようなパスになっていますが,PyPIに公開する際には下層のディレクトリが用いられるため,numpyやtorchのようなパスになると思います.
import numpy
import torch
import datetime
import myapp
print(numpy.__file__) # anaconda3/lib/python3.9/site-packages/numpy/__init__.py
print(torch.__file__) # anaconda3/lib/python3.9/site-packages/torch/__init__.py
print(datetime.__file__) # .../anaconda3/lib/python3.9/datetime.py
print(myapp.__file__) # .../myapp/myapp/__init__.py
クラスかどうかを判定
また,同じような名前で module か class か混乱する場合もありますが,次のようにしてクラスかどうかを判定できます.
import datetime
import inspect
datetime.datetime(2022, 12, 1, 16, 0, 33, 889510)
print(inspect.isclass(datetime.datetime)) # True
# methodの判定
print(datetime.datetime.now) # <built-in method now of type object>
相対インポート
package 内の module について,import 時に from .main import func1
などの表記がありますが,これは package 内の moduleA から moduleB を呼び出すために用いる記法であり,メインモジュール (Pythonコマンドで実行するモジュール) や jupyter などで呼び出すことはできません.
相対 import は現在のモジュール名をベースにすることに注意してください。メインモジュールの名前は常に "main" なので、Python アプリケーションのメインモジュールとして利用されることを意図しているモジュールでは絶対 import を利用するべきです。
参考
https://web-tech.binarymacaron.xyz/2022/04/05/228/
https://zenn.dev/karaage0703/articles/db8c663640c68b