チュートリアル

Python実践編:モジュールとパッケージ

Pythonモジュールパッケージ実践
広告エリア

はじめに

コードを複数のファイルに分割することで、保守性と再利用性が向上します。Pythonのモジュールとパッケージの仕組みを学びましょう。

モジュールとは

1つのPythonファイル(.py)がモジュールです。

# utils.py
def greet(name):
    return f"こんにちは、{name}さん!"

def add(a, b):
    return a + b

PI = 3.14159

import文の使い方

モジュール全体をインポート

import utils

print(utils.greet("太郎"))
print(utils.add(1, 2))
print(utils.PI)

特定の要素だけインポート

from utils import greet, PI

print(greet("太郎"))
print(PI)
# add は使えない

別名をつける

import utils as u
from utils import greet as hello

print(u.add(1, 2))
print(hello("太郎"))

全てをインポート(非推奨)

from utils import *

# 名前空間が汚染されるため非推奨

標準ライブラリの活用

# よく使う標準ライブラリ
import os
import sys
import json
import datetime
import random
import math
import re
import collections
from pathlib import Path

# 使用例
print(os.getcwd())
print(sys.version)
print(datetime.datetime.now())
print(random.randint(1, 100))
print(math.sqrt(16))

パッケージとは

複数のモジュールをディレクトリにまとめたものがパッケージです。

mypackage/
├── __init__.py
├── utils.py
├── models.py
└── subpackage/
    ├── __init__.py
    └── helpers.py

__init__.pyの役割

パッケージであることを示すファイルです(Python 3.3以降は省略可能だが、推奨)。

# mypackage/__init__.py
from .utils import greet
from .models import User

__all__ = ["greet", "User"]  # from mypackage import * で公開するもの

パッケージのインポート

# パッケージからインポート
from mypackage import greet
from mypackage.models import User
from mypackage.subpackage.helpers import helper_func

# 相対インポート(パッケージ内で使用)
# models.py から
from .utils import greet
from ..other_package import something

パッケージの作成例

calculator/
├── __init__.py
├── basic.py
└── advanced.py
# calculator/basic.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b
# calculator/advanced.py
import math

def power(base, exp):
    return base ** exp

def sqrt(n):
    return math.sqrt(n)
# calculator/__init__.py
from .basic import add, subtract
from .advanced import power, sqrt

__version__ = "1.0.0"
# 使用側
from calculator import add, sqrt

print(add(1, 2))
print(sqrt(16))

import calculator
print(calculator.__version__)

if __name__ == "__main__"

スクリプトとして実行された場合のみ動作するコードを記述します。

# utils.py
def greet(name):
    return f"こんにちは、{name}さん!"

def main():
    print(greet("テスト"))

if __name__ == "__main__":
    # python utils.py で実行した場合のみ動作
    main()

モジュール検索パス

Pythonは以下の順序でモジュールを検索します。

import sys
print(sys.path)
# ['', '/usr/lib/python3.x', ...]

# パスを追加
sys.path.append('/path/to/modules')

環境変数PYTHONPATHでも設定可能です。

仮想環境

プロジェクトごとに依存関係を分離します。

# 仮想環境の作成
python -m venv venv

# 有効化
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate

# パッケージのインストール
pip install requests

# 依存関係の書き出し
pip freeze > requirements.txt

# 依存関係のインストール
pip install -r requirements.txt

# 無効化
deactivate

実践的なプロジェクト構成

my_project/
├── src/
│   └── my_project/
│       ├── __init__.py
│       ├── main.py
│       ├── models/
│       │   ├── __init__.py
│       │   └── user.py
│       └── utils/
│           ├── __init__.py
│           └── helpers.py
├── tests/
│   ├── __init__.py
│   └── test_main.py
├── requirements.txt
├── setup.py
└── README.md
# src/my_project/models/user.py
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

# src/my_project/models/__init__.py
from .user import User

pyproject.toml(モダンな設定)

[project]
name = "my_project"
version = "0.1.0"
description = "My awesome project"
requires-python = ">=3.9"
dependencies = [
    "requests>=2.28.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "black>=23.0.0",
]

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

実践例:設定管理モジュール

# config/__init__.py
import os
import json
from pathlib import Path

class Config:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._load()
        return cls._instance

    def _load(self):
        config_path = Path(__file__).parent / "settings.json"
        if config_path.exists():
            with open(config_path) as f:
                self._data = json.load(f)
        else:
            self._data = {}

        # 環境変数で上書き
        self._data["debug"] = os.getenv("DEBUG", "false").lower() == "true"
        self._data["database_url"] = os.getenv("DATABASE_URL", self._data.get("database_url"))

    def get(self, key, default=None):
        return self._data.get(key, default)

# シングルトンとして使用
config = Config()
# 使用側
from config import config

if config.get("debug"):
    print("デバッグモード")

まとめ

  • モジュール = 1つの.pyファイル
  • パッケージ = ディレクトリにまとめたモジュール群
  • __init__.pyでパッケージの公開APIを定義
  • if __name__ == "__main__"でスクリプト実行時の処理を分離
  • 仮想環境でプロジェクトごとに依存関係を管理
  • pyproject.tomlでモダンなパッケージ設定

次回は仮想環境と依存関係管理について詳しく学びます。

広告エリア