はじめに
最近、チーム内で研究プロジェクトに取り組んでおり、その中で既存のツールの1つがPythonで開発されています。私の目標は、このツールのプロセスを最適化することです。既存のプロセスを理解するために該当の開発者に話を聞き、私の研究に基づいて最適化案を提案し、最後に開発者に実装してもらうことも可能です。しかし、プログラマー心理として、何でも自分で理解したいと思ってしまうため、探求の道を歩み始めました。
インターネットで情報を検索し、PyCharm開発ツールをダウンロードしていくつかの環境設定を行い、いざ作業開始となりました。Python言語に触れたことがなかったため、コードフォルダを開いてもまったく理解できず、大量の.pyファイルがあり、どこから手をつければよいかわかりませんでした。私はC#出身で、少しだけC/C++を書いたことがあるので、Main()関数のようなものがプログラムのエントリポイントであると考えていました。しかし、ディレクトリ全体を調べても手がかりは見つからず、その後あれこれ試行錯誤を始めました。
なお、本稿はPython初心者の学習メモに過ぎず、正確または完全であるとは限りません。ご指摘いただければ幸いです。
新しいツールや言語に触れるたびに、不思議な喜びを感じ、既存の知識を使って推論するのが楽しいので、ここに記録します。
順次実行
Pythonの世界では、各.pyファイルがモジュールであり、コンソールでファイル名を入力するとそのモジュールが呼び出されます。
モジュールはバッチファイル(.bat)に似ており、文は順に実行されます。
この点は私の最初の想像とは異なり、C#などの言語のようにファイル内がクラスで構成されていると思っていましたが、実際は違いました。
まず、DドライブのルートにTest1.pyというファイルを作成し、内容は次の通りです。
print("Test1 First")
print("Test1 Second")
次に、コンソールでディレクトリをDドライブに移動し、Test1.pyモジュールを起動します。
D:\>python Test1.py
Test1 First
Test1 Second
うん、期待通りです。次にモジュール間の呼び出しを試します。
DドライブにTest2.pyを作成し、その中でTest1.pyモジュールを呼び出します。
import Test1
print("Test2 First")
print("Test2 Second")
コンソールでTest2.pyを起動すると、出力結果は次のようになります。
D:\>python Test2.py
Test1 First
Test1 Second
Test2 First
Test2 Second
Test2.pyではimport Test1文が先にあるため、Test1モジュールをインポートするときにその中の文が実行され、その結果Test1の出力が先に表示されます。
もしimport Test1を後に置くと、Test1の出力も後になります。
print("Test2 First")
print("Test2 Second")
import Test1
D:\>python Test2.py
Test2 First
Test2 Second
Test1 First
Test1 Second
関数定義
モジュール内のコードをより柔軟にすることはできないでしょうか?単に順次実行するだけでなく、必要に応じて呼び出すこともできます。C#言語の関数のように。
上記のprintは組み込み関数のようです。調べてみると、Pythonでの関数の定義は次の通りです。
def 関数名(パラメータリスト):
関数本体
早速試してみます。Test1.py内にSayHello関数を定義します。
print("Test1 First")
print("Test1 Second")
def SayHello():
print("Hello World")
SayHello()
print("Test1 Third")
出力結果:
D:\>python Test1.py
Test1 First
Test1 Second
Hello World
Test1 Third
うん、期待通り、順に実行されています。
もしSayHello()関数を定義するだけで呼び出さなければ、"Hello World"の行は出力されません。
次に、モジュール間での関数呼び出しを試します。Test2.pyを修正します。
import Test1
print("Test2 First")
print("Test2 Second")
Test1.SayHello()
出力結果:
D:\>python Test2.py
Test1 First
Test1 Second
Hello World
Test1 Third
Test2 First
Test2 Second
Hello World
はは、そうです、そうです。最後の行の"Hello World"はTest2.py内のTest1.SayHello()文によって出力されています。
3行目の"Hello World"は、import Test1のときにTest1モジュールが出力したものです。
__main__
関数の定義とモジュール間の呼び出しについて理解したところで、次に生じた疑問は、「プログラム/モジュールのエントリポイントはどこにあるのか」ということです。
資料を検索したところ、__name__属性が見つかりました。まずは次のコードをテストします。Test1.pyを修正します。
def SayHello():
print("Hello World")
def SayBye():
print("Bye World")
SayHello()
if(__name__=="__main__"):
print("Main")
SayBye()
コンソールでTest1.pyを直接起動します。
D:\>python Test1.py
Hello World
Main
Bye World
うん、理解しやすいです。順に実行され、if(__name__=="__main__")条件を満たしたのでMainが出力されました。
ちょっと待って、別の方法として、Test2.pyを介してTest1.pyを間接的に呼び出してみます。まずTest2.pyを修正します。
import Test1
print("Test2 First")
print("Test2 Second")
そしてTest2.pyを起動して結果を見てみます。
D:\>python Test2.py
Hello World
Bye World
Test2 First
Test2 Second
えっ、Mainが出力されていませんね。なるほど、面白いです。菜鳥教程の説明を見つけました。
各モジュールには
__name__属性があり、その値が__main__である場合、そのモジュール自身が実行されていることを示し、そうでない場合はインポートされたことを示します。
この__name__属性は理解しやすいです。モジュールの予約フィールド(属性)です。しかし、この__main__はどのように理解すればよいのでしょうか?
ここでの__main__は、プログラムのエントリポイント関数と考えることができ、モジュールが直接エントリポイント関数から呼び出された場合、その__name__属性の値はmainとなり、そうでない場合はモジュールのファイル名となります。
def SayHello():
print("Hello World")
def SayBye():
print("Bye World")
SayHello()
if(__name__=="__main__"):
print("Main")
else:
print(__name__)
SayBye()
D:\>python Test2.py
Hello World
Test1
Bye World
Test2 First
Test2 Second
まとめ
本稿では、Pythonモジュールの基本的な特性について説明しました。扱った知識は非常に初歩的であり、個人の学習過程を記録したものに過ぎません。
新しいツールや言語に触れるたびに、不思議な喜びを感じ、既存の知識を使って推論するのが楽しいので、ここに記録します。
最後に、菜鳥教程のモジュールに関する重要な説明を引用します。
- モジュールはメソッド定義以外に、実行可能なコードを含めることもできます。このコードは通常、モジュールの初期化に使用されます。
- モジュールは何度importを実行しても、一度だけインポートされます。これにより、モジュールが何度も実行されるのを防ぎます。
- モジュールは他のモジュールをインポートできます。モジュールの先頭でimportを使用してモジュールをインポートするのは慣例ですが、必須ではありません。