Pythonのexec関数の使い方|引数や変数の使い方を解説する

スポンサーリンク

そこで今日はこのチュートリアルで、Pythonのexec()メソッドについて知っておきましょう。

スポンサーリンク

Python の exec() メソッド

基本的に、Python の exec() メソッドは渡されたコード群を文字列の形で実行します。

動的な実行を実質的にサポートするため、非常に便利なメソッドです。

このメソッドのシンタックスは以下のとおりです。

exec(object, globals, locals)

ここで、object は文字列、オープンファイルオブジェクト、またはコードオブジェクトです。

  • 文字列の場合、文字列は Python の一連のステートメントとして解析され、実行されます (シンタックスエラーが発生しない限り)。
  • オープンファイルの場合 – ファイルはEOFまで解析され、実行されます。
  • コードオブジェクトの場合 – 単純に実行されます。

そして、オプションの引数 globalslocals は、グローバル変数とローカル変数に使用する辞書でなければなりません。

さて、exec() メソッドの基本的な考え方は理解できたと思いますので、例題を通してその動作を理解しましょう。

>>> exec("print('Hey!')")
Hey!
>>> exec("print(6+4)")
10

上のコードから明らかなように、exec() メソッドによって print() 文が正常に実行され、望みの結果が得られています。

Python の exec() メソッドを使った作業

それでは、 globalslocals パラメータを指定した場合と指定しない場合の exec() メソッドの動作について、いくつかの例を挙げて説明します。

1. グローバルパラメータとローカルパラメータを使用しない場合

前の例では、Pythonの exec() メソッドにオブジェクトの引数を渡して、いくつかの命令を実行しただけでした。

しかし、カレントスコープにある名前を見ることはできませんでした。

ここで、dir()メソッドを使って、exec()メソッドを呼び出す前に、現在のメソッドとmath` モジュールを含む名前のリストを取得してみましょう。

from math import *
 
exec("print(pow(2, 5))")
 
exec("print(dir())")

結果は以下の通りです。

32.0
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

ご覧の通り、 builtins を含む様々なメソッドと math モジュールがカレントスコープに存在し、 Python の exec() メソッドが使用可能な状態になっています。

このことは、動的なPythonコードの実行を考えたときに、大きなセキュリティの問題を提起します。

ユーザは、コンピュータをクラッシュさせる可能性のあるシステムコマンドにアクセスするために、いくつかのモジュールをインクルードするかもしれません。

globalslocalsパラメータを使用すると、文字通り、exec()` がアクセスしたいメソッドを越えてアクセスすることを制限することができます。

2. globals パラメータを使用する

では、Pythonの exec() メソッドにglobalsパラメータを指定した場合、どのように使用することができるかを見てみましょう。

Pythonでは、組み込みモジュールから exec() メソッドにアクセスさせたいメソッドだけを(辞書形式で)渡して指定することができます

def squareNo(a):
    return a*a
 
exec('print(squareit(10))',{"__builtins__":{"squareit": squareNo, "print": print}})
 
exec("print(dir())")

結果は以下の通りです。

100
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'squareNo']

上記のコードでは、squareNo() (squareit というカスタム名にマップされる) と print() というメソッドを含む辞書を渡しています。

なお、組み込みメソッドから他のメソッドを使用すると、TypeError が発生します。

3. locals パラメータを使用する場合

locals` パラメータ (辞書) を指定すると、デフォルトでは、明示的に除外するまでは、すべての組み込みメソッドが使用可能になります。

以下の例では、 locals 辞書が指定されていますが、すべての組み込みメソッドと math モジュールのメソッドが現在のスコープで使用可能です。

from math import *
def squareNo(a):
    return a*a
 
#global And local parameters
exec('print(pow(4,3))', {"squareit": squareNo, "print": print})
 
exec("print(dir())")

出力されます。

64
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'squareNo', 'tan', 'tanh', 'tau', 'trunc']

したがって、今度はビルトインを明示的に除外します。

from math import *
 
def squareNo(a):
    return a*a
 
#explicitly excluding built-ins
exec('print(pow(4,3))', {"__builtins__": None},{"squareit": squareNo, "print": print})
 
exec("print(dir())")

結果を出力すると、以下の様になります。

出力:

Traceback (most recent call last):
  File "C:/Users/sneha/Desktop/test.py", line 7, in <module>
    exec('print(pow(4,3))', {"builtins": None},{"squareit": squareNo, "print": print})
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable

上記のコードでは、exec() メソッドがpassed(locals)メソッドのみを使用するように制限しているため、実質的に pow() メソッドはアクセス不能になっています。

そのため、実行中に TypeError が発生します。

Pythonにおけるexec()とeval()の比較

eval()exec()` の間には、ほぼ同じ処理を行うにもかかわらず、2つの大きな違いがあります。

  1. eval() は一つの式しか実行できませんが、exec() はループ、 if-else 文、関数、 class 定義など、動的に生成された文やプログラムを実行するために使用できます。
  2. eval() は特定の式を実行した後の値を返しますが、exec() は基本的に何も返さず、値を無視します。

まとめ

というわけで、今日はここまで。

Pythonの exec() メソッドの使い方と同様に、動作も明確に理解していただけたと思います。

さらにPythonの exec() に関連する質問があれば、下のコメント欄で気軽に質問してください。

参考文献

  • exec 文 – Python ドキュメント。
  • eval、exec、compileの違いは何ですか?- スタックオーバーフローの質問です。
  • Pythonのexec() – JournalDevポスト。
タイトルとURLをコピーしました