1. packageにすると何が良いのか
- pipでインストールできる
- githubに置けるのでバージョン管理がやりやすい
- CIがやりやすい
- PyPIに置けば誰かの役に立つこともできちゃう
2. packageをつくる
ファイル構成
最小構成は以下のようになります
myapp/ ← プロジェクトのフォルダ(フォルダ名は何でも良いです)
├ setup.py ← packageの情報を書くファイル
└ myapp/ ← このフォルダ名でimportするようになります
├ __init__.py ← フォルダ内のクラスやメソッドを定義するファイル
└ main.py ← 実際にコードを書くファイル(ファイル名は自由)
setup.py
packageのメタ情報を書くファイルでpipはこれに従ってインストールします
from setuptools import setup, find_packages
setup(
name='myapp', # パッケージ名(pip listで表示される)
version="0.0.1", # バージョン
description="sample of minimum package", # 説明
author='haneya', # 作者名
packages=find_packages(), # 使うモジュール一覧を指定する
license='MIT' # ライセンス
)
main.py
実際のコードを書くファイルで、ファイル名は任意です
複数ファイルに分けて書く事もできます
def func1():
print('func1() is executed')
__init__.py
packageで使う関数、クラス、定数などが、どのファイルにあるか書いておくファイルです
from .main import func1 # 同じフォルダのmain.pyにあるfunc1をimport
__all__ = ['func1'] # func1をpackageから呼べるようにする
インストールする
ここまで出来たら、setup.pyがあるフォルダから以下コマンドを実行します
pip install -e .
インストール出来たか確認しましょう
> pip list
Package Version Location
------------ ------------------- -----------------------------------------------------------------------------
myapp 0.0.1 c:\users\hoge\myapp
使ってみる
> python
関数はmyappフォルダから順にたどって呼ぶ関数にたどり着けば呼ぶことが出来ます
>>> import myapp
>>> myapp.main.func1()
func1() is executed
また、myappフォルダ内の__init__.pyで__all__に割り当てているのでmyapp直下から呼ぶことも出来ます
>>> import myapp
>>> myapp.func1()
func1() is executed
上手くインストールできたようです
3. コードを複数ファイルに分ける
コード量が増えてくるとファイルを分けたくなりますが、その場合も__init__.pyに書いてやれば1つのパッケージのモジュールとして扱ってくれます
myapp/
├ setup.py
└ myapp/
├ __init__.py ← sub.pyについて書き足す
├ main.py
└ sub.py ← 追加したファイル
def func2():
print('func2() is executed')
from .main import func1
from .sub import func2 # 追加
__all__ = ['func1', 'func2'] # 'func2'を追加
先程と同様にsetup.pyがあるフォルダから以下コマンドを実行すればインストールされます
pip install -e .
普通にたどって行って呼ぶ場合は以下のようになります
>>> import myapp
>>> myapp.main.func1()
func1() is executed
>>> myapp.sub.func2()
func2() is executed
また、myappフォルダ内の__init__.pyで__all__に割り当てているのでmyapp直下から呼ぶことも出来ます
>>> import myapp
>>> myapp.func1()
func1() is executed
>>> myapp.func2()
func2() is executed
4. 階層構造をつくる
階層をつくって役割ごとに関数を分けて整理したい場合も、普通にフォルダで階層を作ってやればそのように読み込んでくれます
myapp/
├ setup.py
└ myapp/
├ app1/ ← 追加
| ├ __init__.py ← 追加
| └ main.py ← 追加
├ __init__.py
├ main.py
└ sub.py
def func3():
print('func3() is executed')
from .main import func3
__all__ = ['func3']
同様にフォルダをたどって行って呼ぶ場合は以下のようになります
>>> import myapp
>>> myapp.main.app1.main.func3()
func3() is executed
また、myapp/app1フォルダ内の__init__.pyで__all__に割り当てているのでmyapp.app1から呼ぶことも出来ます
>>> import myapp
>>> myapp.app1.func3()
5. githubに置いたプロジェクトからインストールする
区切りのいいところまで開発したらgithubに置くのが良いと思います
リポジトリURLが https://github.com/haneya-studio/test_package である場合は以下のようにインストールすることができます。
pip install git+https://github.com/haneya-studio/test_package.git
6. wheelに固める
以下のようにしてwhlファイルに固めることが出来ます
python setup.py bdist_wheel
上記を実行するとdistフォルダ内に以下のように書き出されます
以下のようにすればpipがインストールしてくれます
pip install myapp-0.0.2-py3-none-any.whl
7. データファイルをパッケージに含める
setuptools.find_packages()はPythonソースコードしか含めてくれないので、機械学習モデルやGUIのアイコンなどのファイルをパッケージに含めたい場合は別途MANIFEST.inに書く必要があります
myapp/
├ setup.py
├ MANIFEST.in ← 含めるファイルの指定を書く
└ myapp/
├ txt/
| └ hoge.txt ← 含めたいファイル
├ __init__.py
└ fuga.py ← ここからhoge.txtを使います
7-1. MANIFEST.in
MANIFEST.inに以下のようにPATHを書いてincludeすればパッケージに含めてくれます
include myapp/txt/hoge.txt
フォルダ内のファイルをまとめてincludeしたい場合は以下のようにも書けます
recursive-include myapp/txt *
7-2. 組み込んだファイルの使い方
fuga.pyからhoge.txtへの相対PATHは './txt/hoge.txt' ですが、パッケージとしてimportして使う際には呼び出した側のワーキングディレクトリからの相対PATHと扱われてしまうので当然ながら上手く読み込めません。__file__で当該ファイルの場所を調べて、そこを基準に読みにいくのが良さげです。
import os
def fuga():
path = os.path.dirname(os.path.abspath(__file__)) + "/txt/hoge.txt"
print(path)
print(os.path.isfile(path))
>>> myapp.fuga()
C:\Users\jjaka\マイドライブ\python\_github\test_package\myapp/txt/hoge.txt
True
ちゃんと見つけることが出来ました。os.listdir()なんかも出来ますので扱いは普通のファイルと同じです。
参考にした記事