はじめに
このセクションでは、モジュールの概念と複数のファイルにまたがる関数の使用方法について説明します。
このセクションでは、モジュールの概念と複数のファイルにまたがる関数の使用方法について説明します。
任意の Python ソースファイルはモジュールです。
## foo.py
def grok(a):
...
def spam(b):
...
import文はモジュールを読み込み、実行します。
## program.py
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
モジュールは名前付きの値のコレクションであり、時には「名前空間」と呼ばれます。名前は、ソースファイルに定義されたすべてのグローバル変数と関数です。インポート後、モジュール名が接頭辞として使用されます。したがって「名前空間」です。
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
モジュール名は直接ファイル名に関連付けられています(foo -> foo.py)。
グローバル スコープで定義されたすべてのものが、モジュール名前空間を構成します。同じ変数 x を定義する 2 つのモジュールを考えてみましょう。
## foo.py
x = 42
def grok(a):
...
## bar.py
x = 37
def spam(a):
...
この場合、x の定義は異なる変数を指します。1 つは foo.x で、もう 1 つは bar.x です。異なるモジュールは同じ名前を使用でき、それらの名前は互いに衝突しません。
モジュールは孤立しています。
モジュールは、その内部に定義されたすべてのコードの囲い込む環境を形成します。
## foo.py
x = 42
def grok(a):
print(x)
グローバル 変数は常に囲い込むモジュール(同じファイル)にバインドされます。各ソースファイルは独自の小宇宙です。
モジュールがインポートされると、モジュール内の すべての文が順番に実行 され、ファイルの末尾に達するまで続きます。モジュール名前空間の内容は、実行プロセスの終了時にまだ定義されているすべての グローバル 名前です。グローバルスコープでタスクを実行するスクリプト文(印刷、ファイルの作成など)がある場合、インポート時にそれらが実行されるのを見ることができます。
import as 文インポートする際にモジュールの名前を変更することができます。
import math as m
def rectangular(r, theta):
x = r * m.cos(theta)
y = r * m.sin(theta)
return x, y
通常のインポートと同じように機能します。ただ、その 1 つのファイル内でモジュールの名前を変更するだけです。
from モジュール importこれは、モジュールから選択されたシンボルを抽出し、それらをローカルで利用可能にします。
from math import sin, cos
def rectangular(r, theta):
x = r * cos(theta)
y = r * sin(theta)
return x, y
これにより、モジュールの一部を使用する際にモジュール接頭辞を入力する必要がなくなります。頻繁に使用する名前に便利です。
インポートのバリエーションは、モジュールの動作方法を 変更しません。
import math
## vs
import math as m
## vs
from math import cos, sin
...
具体的には、import は常に ファイル全体を実行 し、モジュールは依然として孤立した環境です。
import module as 文は、ローカルでの名前のみを変更しています。from math import cos, sin 文は、依然としてバックグラウンドで math モジュール全体を読み込みます。それは、完了後にモジュールから cos と sin の名前をローカル空間にコピーするだけです。
各モジュールは 一度だけ読み込みと実行 されます。注:繰り返しインポートは、以前に読み込まれたモジュールへの参照を返すだけです。
sys.modules は、すべての読み込まれたモジュールの辞書です。
>>> import sys
>>> sys.modules.keys()
['copy_reg', '__main__','site', '__builtin__', 'encodings', 'encodings.encodings', 'posixpath',...]
>>>
注意:モジュールのソースコードを変更した後に import 文を繰り返すと、一般的な混乱が発生します。モジュールキャッシュ sys.modules のため、繰り返しインポートは常に以前に読み込まれたモジュールを返します。たとえ変更が行われていてもです。Python に修正されたコードを読み込む最も安全な方法は、インタプリタを終了して再起動することです。
Python は、モジュールを探す際にパスリスト(sys.path)を参照します。
>>> import sys
>>> sys.path
[
'',
'/usr/local/lib/python36/python36.zip',
'/usr/local/lib/python36',
...
]
通常、現在の作業ディレクトリが最初になります。
前述の通り、sys.path には検索パスが含まれています。必要に応じて手動で調整することができます。
import sys
sys.path.append('/project/foo/pyfiles')
パスは環境変数を通じても追加することができます。
% env PYTHONPATH=/project/foo/pyfiles python3
Python 3.6.0 (default, Feb 3 2017, 05:53:21)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)]
>>> import sys
>>> sys.path
['','/project/foo/pyfiles',...]
一般的なルールとして、モジュール検索パスを手動で調整する必要はありません。ただし、通常の場所にない、または現在の作業ディレクトリから簡単にアクセスできない Python コードをインポートしようとしている場合には、このようなことが発生することがあります。
このモジュールに関する演習では、適切な環境で Python を実行していることを確認することが非常に重要です。モジュールは、新しいプログラマーにとって、現在の作業ディレクトリや Python のパス設定に関連する問題を引き起こすことがよくあります。このコースでは、すべてのコードを ~/project ディレクトリに書いていることを前提としています。最善の結果を得るには、インタプリタを起動するときにもそのディレクトリにいることを確認する必要があります。そうでない場合は、~/project を sys.path に追加する必要があります。
セクション 3 では、CSV データファイルの内容を解析するための汎用関数 parse_csv() を作成しました。
今回は、その関数を他のプログラムでどのように使用するかを見ていきます。まず、新しいシェルウィンドウを開きます。すべてのファイルがあるフォルダに移動します。それらをインポートしましょう。
Python の対話型モードを起動します。
$ python3
Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
これを行ったら、以前に書いたプログラムのいくつかをインポートしてみましょう。以前とまったく同じ出力が表示されるはずです。強調するために、モジュールをインポートするとそのコードが実行されます。
>>> import bounce
... 出力を見る...
>>> import mortgage
... 出力を見る...
>>> import report
... 出力を見る...
>>>
これがうまくいかない場合は、おそらく Python を間違ったディレクトリで実行しています。次に、fileparse モジュールをインポートして、それに関するヘルプを取得してみましょう。
>>> import fileparse
>>> help(fileparse)
... 出力を見る...
>>> dir(fileparse)
... 出力を見る...
>>>
モジュールを使ってデータを読み取ってみましょう:
>>> portfolio = fileparse.parse_csv('/home/labex/project/portfolio.csv',select=['name','shares','price'], types=[str,int,float])
>>> portfolio
... 出力を見る...
>>> pricelist = fileparse.parse_csv('/home/labex/project/prices.csv',types=[str,float], has_headers=False)
>>> pricelist
... 出力を見る...
>>> prices = dict(pricelist)
>>> prices
... 出力を見る...
>>> prices['IBM']
106.28
>>>
モジュール名を含める必要がないように、関数をインポートしてみましょう:
>>> from fileparse import parse_csv
>>> portfolio = parse_csv('/home/labex/project/portfolio.csv', select=['name','shares','price'], types=[str,int,float])
>>> portfolio
... 出力を見る...
>>>
セクション 2 では、次のような株価レポートを生成する report.py というプログラムを書きました。
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
そのプログラムを取り出して、入力ファイルの処理をすべて fileparse モジュールの関数を使って行うように修正します。そのために、fileparse をモジュールとしてインポートし、read_portfolio() 関数と read_prices() 関数を parse_csv() 関数を使うように変更します。
この演習の冒頭の対話型の例を参考にしてください。その後、以前とまったく同じ出力が得られるはずです。
セクション 1 では、ポートフォリオを読み取り、そのコストを計算する pcost.py というプログラムを書きました。
>>> import pcost
>>> pcost.portfolio_cost('/home/labex/project/portfolio.csv')
44671.15
>>>
pcost.py ファイルを修正して、report.read_portfolio() 関数を使用するようにします。
この演習が終わったら、3 つのプログラムができているはずです。汎用の parse_csv() 関数を含む fileparse.py。きれいなレポートを生成するだけでなく、read_portfolio() 関数と read_prices() 関数も含む report.py。そして最後に、ポートフォリオのコストを計算するが、report.py プログラム用に書かれた read_portfolio() 関数を利用する pcost.py。
おめでとうございます!あなたはモジュールの実験を完了しました。あなたのスキルを向上させるために、LabEx でさらに多くの実験を行って練習することができます。