コース進捗 コース目次 24レッスン中 24件を公開中
Pythonの基礎
データとコレクション
信頼できるプログラムを作る
オブジェクトでモデル化する
プロフェッショナルなPython
上級Python
コードをラベル付きの引き出しへ入れよう
長いPythonファイルは、大きなおもちゃ箱のようです。全部入っていても、必要なものを探しにくくなります。Pythonでは、仕事ごとにラベル付きの引き出しを作れます。
-
モジュール
importできる1つの
.py - importパッケージ モジュールをまとめたフォルダー
- ディストリビューション pipでインストールする単位
- 仮想環境 1プロジェクト用のPythonの家
似ていますが、同じものではありません。importはモジュールやimportパッケージを読み込みます。python -m pip install ...が入れる単位はディストリビューションです。1つのディストリビューションが複数のimportパッケージを提供することもあり、インストール名とimport名が一致する保証もありません。
名前をこぼさずにimportしよう
import statisticsは、1つのモジュールオブジェクトを名前へ結び付けます。ドットを見ると、道具の出所が分かります。
import statistics
minutes = [20, 30, 40]
print(statistics.mean(minutes))
print(statistics.median(minutes))
出力:
30
30
このモジュール接頭辞が名前空間です。自分のmeanとstatistics.meanを同時に使えます。必要な名前だけを読み込む書き方もあります。
from statistics import mean as average
print(average([10, 20, 30]))
別名は意味が明確になるときだけ使います。from statistics import *は避けましょう。ワイルドカードでは、どの名前が入り、どの古い名前が置き換わったか見えません。
1つのPythonプロセスで最初にモジュールを正常にimportすると、最上位のコードが上から下へ実行されます。その後の通常のimportは、キャッシュされた同じモジュールを使います。最上位には主に関数、クラス、定数の定義を置き、入力やアプリ本体を始めないようにします。
- 最初のimport 最上位の定義を実行
- 覚える モジュールをキャッシュ
- mainガード 意図したときだけアプリを実行
def main():
print("学習レポートを開始します。")
if __name__ == "__main__":
main()
入口のファイルとして実行すると、__name__は"__main__"なので表示されます。importされた場合はモジュールのimport名になり、main()は使えるようになりますが、自動実行されません。
パッケージに1つの玄関を作ろう
通常のパッケージは、__init__.pyと関連モジュールを含むディレクトリです。
project/
├── study_report/
│ ├── __init__.py
│ ├── calculations.py
│ └── __main__.py
└── tests.py
__init__.pyは通常のパッケージであることを示し、最初のパッケージimport時に実行されます。空でもよく、小さな公開インターフェースを再公開してもかまいません。パッケージ内の先頭のドットは「このパッケージから」という意味です。
from .calculations import summarize
__all__ = ["summarize"]
__all__は意図するインターフェースを説明しますが、privacyやsecurityの壁ではありません。AがBをimportし、BがAをimportする循環importも避けます。共有処理を低い層へ移すか、より明確な境界で値を渡してください。
-
import
__init__.pyがAPIを開く - 相対import ドットが文脈を保つ
-
python -m
__main__.pyを実行
パッケージを含むディレクトリから実行します。
python -m study_report
python study_report/__main__.pyでは内部ファイルを単独のスクリプトとして扱うため、相対importが壊れることがあります。
実行できる学習レポートパッケージを作ろう
上の構成を作り、study_report/calculations.pyへ保存します。
def summarize(sessions):
totals = {}
for topic, minutes in sessions:
totals[topic] = totals.get(topic, 0) + minutes
return sorted(totals.items())
study_report/__init__.py:
from .calculations import summarize
__all__ = ["summarize"]
study_report/__main__.py:
from . import summarize
def main():
sessions = [("Python", 45), ("Git", 40), ("Python", 30)]
print("学習レポート")
for topic, minutes in summarize(sessions):
print(f"- {topic}:{minutes}分")
if __name__ == "__main__":
main()
パッケージと同じ場所のtests.py:
from study_report import summarize
assert summarize([]) == []
assert summarize([("Python", 20), ("Python", 30)]) == [("Python", 50)]
print("テストに合格しました。")
python tests.py、続けてpython -m study_reportを実行します。期待される出力:
テストに合格しました。
学習レポート
- Git:40分
- Python:75分
テストのimportではレポートが表示されません。パッケージは安定した操作を1つ公開し、見えるアプリ動作は__main__.pyが担当します。
出所を確認し、プロジェクトへ家を用意しよう
ローカルのstatistics.py、json.py、pathlib.pyが、本物のライブラリを隠すことがあります。Pythonが読んだ場所を調べます。
import statistics
print(statistics.__name__)
print(statistics.__file__)
2行目はマシンによって違います。自分のプロジェクトではなく、期待するライブラリを指すか確認してください。アプリ各所でsys.pathを書き換えず、ファイル名や構成を直します。
プロジェクトルートで分離環境を作ります。
python -m venv .venv
source .venv/bin/activate
python -c "import sys; print(sys.prefix != sys.base_prefix)"
Windows PowerShellは.venv\Scripts\Activate.ps1です。確認結果はTrueになります。必要なディストリビューションはpython -m pip install distribution-nameで入れ、deactivateで終了します。.venv/を.gitignoreへ加え、環境を移動・コミットせず、記録した手順から作り直します。venvが分離するのはインストール済み配布物であり、データファイルやimport設計ではありません。
3つの小さなチャレンジと次への準備
- 安全なimport。 温度変換を
conversions.pyへ移し、表示をweather_report.pyのmainガード内へ置きます。importだけでは何も表示されないことを確認します。 - 新しいパッケージ。 公開する集計関数1つ、
__init__.py、__main__.pyを持つreading_logを作り、python -m reading_logで動かします。 - 環境の証明。
.venvを作って有効化し、prefixの確認がTrueになることを確かめ、無効化してGitの対象外だと確認します。
注意点:ワイルドカードは名前を隠し、最上位の実処理はimportの副作用になります。内部ファイルの直接実行はパッケージ文脈を失い、ローカルファイル名は別のモジュールを読ませることがあります。
- モジュール、importパッケージ、ディストリビューション、venvを区別できる
- 名前空間と最初のimport時の実行を説明できる
- mainガードと小さな
__init__.pyインターフェースを使える -
python -mでパッケージを実行し、テストできる - importしたモジュールの出所を調べられる
-
.venvを作成、確認、終了、再作成できる - 3つの小さなチャレンジを終えた
次章では、pathlib、with、CSV、JSONを使い、整理したコードへ安全なローカル保存を加えます。