大規模な Python プログラムの整理方法

Beginner

This tutorial is from open-source community. Access the source code

はじめに

もし大きなプログラムを書く場合、一番上のレベルで独立したファイルの大きなコレクションとして整理するのはあまり望ましくありません。このセクションでは、パッケージの概念を紹介します。

モジュール

どの Python ソースファイルもモジュールです。

## foo.py
def grok(a):
 ...
def spam(b):
 ...

import 文はモジュールを読み込み、そして 実行 します。

## program.py
import foo

a = foo.grok(2)
b = foo.spam('Hello')
...

パッケージとモジュールの違い

より大きなコードのコレクションの場合、モジュールをパッケージに整理するのが一般的です。

## これから
pcost.py
report.py
fileparse.py

## これにする
porty/
    __init__.py
    pcost.py
    report.py
    fileparse.py

名前を選んでトップレベルのディレクトリを作成します。上の例では porty です(明らかにこの名前を選ぶことが一番重要な最初のステップです)。

ディレクトリに __init__.py ファイルを追加します。これは空でもかまいません。

ソースファイルをディレクトリに配置します。

パッケージの使用

パッケージはインポートの名前空間として機能します。

これは、今では階層的なインポートが可能になることを意味します。

import porty.report
port = porty.report.read_portfolio('portfolio.csv')

インポート文には他にもバリエーションがあります。

from porty import report
port = report.read_portfolio('portfolio.csv')

from porty.report import read_portfolio
port = read_portfolio('portfolio.csv')

二つの問題

このアプローチには主に二つの問題があります。

  • 同じパッケージ内のファイル間のインポートが機能しなくなります。
  • パッケージ内に配置されたメインスクリプトが機能しなくなります。

ですから、基本的にすべてが機能しなくなります。でも、それ以外は機能します。

問題:インポート

同じパッケージ内のファイル間でのインポートでは、インポートにパッケージ名を含める必要があります。構造を覚えておいてください。

porty/
    __init__.py
    pcost.py
    report.py
    fileparse.py

修正されたインポートの例。

from porty import fileparse

def read_portfolio(filename):
    return fileparse.parse_csv(...)

すべてのインポートは 絶対的 であり、相対的ではありません。

import fileparse    ## エラーになります。fileparse が見つかりません

...

相対インポート

パッケージ名を直接使用する代わりに、. を使って現在のパッケージを参照することができます。

from. import fileparse

def read_portfolio(filename):
    return fileparse.parse_csv(...)

構文:

from. import modname

これにより、パッケージ名を変更することが簡単になります。

問題:メインスクリプト

パッケージのサブモジュールをメインスクリプトとして実行するとエラーになります。

$ python porty/pcost.py ## エラーになります
...

理由:単一のファイルで Python を実行しているため、Python はパッケージ構造の残りの部分を正しく認識できません(sys.pathが間違っています)。

すべてのインポートがエラーになります。これを修正するには、-m オプションを使用して、別の方法でプログラムを実行する必要があります。

$ python -m porty.pcost ## 正常に動作します
...

__init__.py ファイル

これらのファイルの主な目的は、モジュールをまとめることです。

例:関数の統合

## porty/__init__.py
from.pcost import portfolio_cost
from.report import portfolio_report

これにより、インポート時に名前が トップレベル に表示されます。

from porty import portfolio_cost
portfolio_cost('portfolio.csv')

階層的なインポートを使う代わりに。

from porty import pcost
pcost.portfolio_cost('portfolio.csv')

スクリプトの別の解決策

前述の通り、パッケージ内のスクリプトを実行するには、-m package.module を使用する必要があります。

$ python3 -m porty.pcost portfolio.csv

別の方法もあります。新しいトップレベルのスクリプトを書きます。

#!/usr/bin/env python3
## pcost.py
import porty.pcost
import sys
porty.pcost.main(sys.argv)

このスクリプトはパッケージの 外側 にあります。たとえば、ディレクトリ構造を見ると:

pcost.py       ## トップレベルのスクリプト
porty/         ## パッケージディレクトリ
    __init__.py
    pcost.py
  ...

アプリケーション構造

コードの組織化とファイル構造は、アプリケーションの保守性にとって重要です。

Python には「万能」のアプローチはありません。ただし、多くの問題に対応できる構造の 1 つは、次のようなものです。

porty-app/
  README.txt
  script.py         ## スクリプト
  porty/
    ## ライブラリコード
    __init__.py
    pcost.py
    report.py
    fileparse.py

トップレベルの porty-app は、その他のすべてのもの(ドキュメント、トップレベルのスクリプト、サンプルなど)のコンテナです。

再び、トップレベルのスクリプト(あれば)はコードパッケージの外に存在する必要があります。1 階層上に。

#!/usr/bin/env python3
## porty-app/script.py
import sys
import porty

porty.report.main(sys.argv)

この時点で、いくつかのプログラムが含まれるディレクトリがあります。

pcost.py          ## ポートフォリオのコストを計算する
report.py         ## レポートを作成する
ticker.py         ## リアルタイムの株価チェッカーを生成する

他の機能を持つさまざまなサポートモジュールがあります。

stock.py          ## 株式クラス
portfolio.py      ## ポートフォリオクラス
fileparse.py      ## CSV解析
tableformat.py    ## フォーマットされたテーブル
follow.py         ## ログファイルを追跡する
typedproperty.py  ## 型付きのクラスプロパティ

このチャレンジでは、コードを整理して共通のパッケージに入れます。

チャレンジ 9.1:単純なパッケージの作成

porty/ というディレクトリを作成し、上記のすべての Python ファイルをその中に入れます。さらに空の __init__.py ファイルを作成し、それを同じディレクトリに置きます。以下のようなファイルのディレクトリができるはずです。

porty/
    __init__.py
    fileparse.py
    follow.py
    pcost.py
    portfolio.py
    report.py
    stock.py
    tableformat.py
    ticker.py
    typedproperty.py

ディレクトリにある __pycache__ ファイルを削除します。これには以前の事前コンパイル済みの Python モジュールが含まれています。新しく始めましょう。

パッケージのいくつかのモジュールをインポートしてみましょう。

>>> import porty.report
>>> import porty.pcost
>>> import porty.ticker

これらのインポートが失敗した場合は、適切なファイルに移動して、モジュールインポートを修正して、パッケージ相対インポートを含めます。たとえば、import fileparse のようなステートメントは、次のように変更される場合があります。

## report.py
from. import fileparse

...

from fileparse import parse_csv のようなステートメントがある場合は、コードを次のように変更します。

## report.py
from.fileparse import parse_csv

...

チャレンジ 9.2:アプリケーションディレクトリの作成

すべてのコードを「パッケージ」に入れるだけでは、アプリケーションには不十分なことが多いです。時にはサポートファイル、ドキュメント、スクリプト、その他のものが必要です。これらのファイルは、上で作成した porty/ ディレクトリの外に存在する必要があります。

porty-app という新しいディレクトリを作成します。チャレンジ 9.1 で作成した porty ディレクトリをそのディレクトリに移動させます。portfolio.csvprices.csv のテストファイルをこのディレクトリにコピーします。さらに、自分に関する情報を含む README.txt ファイルを作成します。あなたのコードは現在、次のように整理されるはずです。

porty-app/
    portfolio.csv
    prices.csv
    README.txt
    porty/
        __init__.py
        fileparse.py
        follow.py
        pcost.py
        portfolio.py
        report.py
        stock.py
        tableformat.py
        ticker.py
        typedproperty.py

コードを実行するには、トップレベルの porty-app/ ディレクトリで作業していることを確認する必要があります。たとえば、ターミナルから:

$ cd porty-app
$ python3
>>> import porty.report
>>>

以前のいくつかのスクリプトをメインプログラムとして実行してみましょう。

$ cd porty-app
$ python3 -m porty.report portfolio.csv prices.csv txt
      Name     Shares      Price     Change
---------- ---------- ---------- ----------
        AA        100       9.22     -22.98
       IBM         50     106.28      15.18
       CAT        150      35.46     -47.98
      MSFT        200      20.89     -30.34
        GE         95      13.48     -26.89
      MSFT         50      20.89     -44.21
       IBM        100     106.28      35.84

$

チャレンジ 9.3:トップレベルのスクリプト

python -m コマンドを使用すると、ときどき少々奇妙な感じがします。パッケージの不具合を単純に処理するトップレベルのスクリプトを書きたい場合があります。上記のレポートを生成する print-report.py というスクリプトを作成します。

#!/usr/bin/env python3
## print-report.py
import sys
from porty.report import main
main(sys.argv)

このスクリプトをトップレベルの porty-app/ ディレクトリに置きます。その場所で実行できることを確認しましょう。

$ cd porty-app
$ python3 print-report.py portfolio.csv prices.csv txt
      Name     Shares      Price     Change
---------- ---------- ---------- ----------
        AA        100       9.22     -22.98
       IBM         50     106.28      15.18
       CAT        150      35.46     -47.98
      MSFT        200      20.89     -30.34
        GE         95      13.48     -26.89
      MSFT         50      20.89     -44.21
       IBM        100     106.28      35.84

$

最終的なコードは、現在、次のような構造になっているはずです。

porty-app/
    portfolio.csv
    prices.csv
    print-report.py
    README.txt
    porty/
        __init__.py
        fileparse.py
        follow.py
        pcost.py
        portfolio.py
        report.py
        stock.py
        tableformat.py
        ticker.py
        typedproperty.py

まとめ

おめでとうございます!あなたはパッケージの実験を完了しました。あなたのスキルを向上させるために、LabEx でさらに多くの実験を行って練習してください。