Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonの__name__と__main__を徹底解説!トップレベルスクリプトとインポート時の動作の違い

Pythonのモジュールオブジェクトには、さまざまな属性が定義されています。これらの属性名は、前后がダブルアンダースコア(__)で囲まれているのが特徴です。中でも特に重要なのが__name__属性です。

Pythonがトップレベルのコードとして実行されているとき、つまり標準入力から読み込まれた場合・スクリプトとして実行された場合・対話型インタプリタ(インタラクティブシェル)から実行された場合には、__name__属性には「__main__」という文字列が設定されます。

対話モードでの__name__の確認

まず、対話型インタプリタで確認してみましょう。

>>> __name__
'__main__'

スクリプトファイルの中でも同様に、__name__の値は「__main__」になります。次のようなスクリプトを実行してみます。

"module docstring"
print ('name of module:', __name__)

出力結果:

name of module: __main__

インポートされたモジュールの場合

一方、インポートされたモジュールの場合は、__name__属性にはそのPythonファイル名(拡張子を除いたもの)が設定されます。

例えば、hello.pyというモジュールをインポートすると以下のようになります。

>>> import hello
>>> hello.__name__
hello

トップレベルで実行したスクリプトでは「__main__」、インポートされたモジュールではファイル名になる——この違いを確認するために、次のスクリプト(moduletest.py)を実行してみましょう。

import hello
print ('name of top level module:', __name__)
print ('name of imported module:', hello.__name__)

出力結果:

name of top level module: __main__
name of imported module: hello

問題点:インポート時に実行したくないコードまで走ってしまう

関数を含むPythonスクリプトには、関数定義以外に実行可能なコードが書かれていることがあります。このようなモジュールをインポートすると、その実行部分まで自動的に実行されてしまいます。

次のmessages.pyには2つの関数が定義されており、実行部分ではユーザーからの入力を引数としてthanks()関数を呼び出しています。

def welcome(name):
    print ("Hi {}. Welcome to TutorialsPoint".format(name))
    return

def thanks(name):
    print ("Thank you {}. See you again".format(name))

name = input('enter name:')
thanks(name)

messages.pyを直接実行すれば、当然ながら感謝メッセージが表示されます。

enter name:Ajit
Thank you Ajit. See you again

ところが、次のようなmoduletest.pyを作成して実行するとどうなるでしょうか。

import messages
print ('name of top level module:', __name__)
print ('name of imported module:', messages.__name__)

実行してみると、インポートしただけでmessages.py内のinput文と関数呼び出しが実行されてしまうことがわかります。

c:\python37>python moduletest.py

出力結果:

enter name:Kishan
Thank you Kishan. See you again
enter name:milind
Hi milind. Welcome to TutorialsPoint

これは2つのスクリプトの出力が混ざった結果です。本来やりたいのは、messagesモジュールから関数だけを利用することであり、モジュール内の実行コードまでは実行したくありません。

解決策:if __name__ == '__main__' の活用

ここで役立つのが、「トップレベルスクリプトの__name__属性の値は__main__になる」という仕組みです。messages.pyを次のように書き換え、input文と関数呼び出しが「__name____main__と等しい場合にのみ」実行されるようにします。

"docstring of messages module"
def welcome(name):
    print ("Hi {}. Welcome to TutorialsPoint".format(name))
    return

def thanks(name):
    print ("Thank you {}. See you again".format(name))

if __name__=='__main__':
    name = input('enter name')
    thanks(name)

このテクニックは、単体で実行できると同時に、他のモジュールからもインポートできるモジュールを作りたい場合に常に活用できます。moduletest.py側には一切変更を加える必要がなく、インポートされた際にはmessagesモジュール内の実行部分が動作しなくなります。

enter name: milind
Hi milind. Welcome to TutorialsPoint

なお、この変更によってもmessages.py独立したスクリプトとして直接実行することは引き続き可能である点に注意してください。つまり、「直接実行時のみ動作し、インポート時には動作しない」という柔軟な制御が実現できるのです。

  1. Python仮想環境の基本:venv・virtualenvによる環境構築と依存関係管理の完全ガイド

    はじめに開発者は、Python標準ライブラリに含まれていないモジュールやパッケージを使用する必要があるプロジェクトに、頻繁に直面します。こうしたパッケージは、特定のアプリケーション専用であることが少なくありません。例えば、マシンには最新バージョンのPython(仮にPython 3.6とします)がインストールされているのに、あるプロジェクトでは特定のバージョン(Python 2.7)が求められるケースを考えてみましょう。この場合、新しいパッケージだけでなく、すでにインストール済みのアプリケーションに対しても特定バージョンの要求が発生します。すると要件同士が衝突し、2.7と3.6のどちらか一方を

  2. Pythonのgetpassモジュールとは?パスワード入力とユーザー名取得の基本を解説

    Pythonの標準ライブラリには、ターミナル上で動作するアプリケーションにおいて、ユーザーの認証情報を検証してから処理を実行したい場合に役立つgetpassモジュールが用意されています。このモジュールには主に2つの関数が定義されており、セキュリティを意識したコマンドラインツールの開発に欠かせない存在です。 getpass() 関数:パスワードを安全に入力する getpass()関数は、ユーザーにパスワードの入力を促すための関数です。デフォルトでは、ターミナルに入力されたキーは画面に表示(エコー)されないため、第三者にパスワードを盗み見られる心配がありません。 また、プロンプトとして表示される