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

Pythonのexec()関数とは?動的コード実行の基本とセキュリティ制御の方法

Pythonのexec()関数を使うと、プログラムの実行中にコードを動的に実行できます。実行したいコードは、文字列またはコードオブジェクトとしてこの関数に渡します。コードオブジェクトの場合はそのまま実行されますが、文字列の場合はまず構文解析(パース)が行われ、構文エラーがないかチェックされます。エラーがなければ、解析された文字列がPythonのステートメントとして実行されます。

exec()関数の構文

exec(object, globals, locals)

各引数の意味は以下の通りです。

  • object − 実行するコードを表す文字列またはコードオブジェクト。

  • globals − 利用可能なグローバルな関数や変数を格納した辞書。

  • locals − 利用可能なローカルな関数や変数を格納した辞書。

文字列を渡す場合

次の例では、1行のコードを文字列としてexec()関数に渡しています。渡された文字列は解析・実行され、結果が出力されます。

x = 9
exec('print(5*x)')

出力

上記のコードを実行すると、次の結果が得られます。

45

コードオブジェクトを渡す場合

次に、複数のステートメントからなるコードブロックを渡す方法を見てみましょう。コードオブジェクトとして渡された場合は直接実行され、結果が出力されます。ここでは、\n(改行)とスペース(インデント)を使って、正しいインデントを持つPythonのコードブロックを作成している点に注目してください。

prog_block = 'x = 3 \nif(x < 5): \n    print(x*x)'
exec(prog_block)

出力

上記のコードを実行すると、次の結果が得られます。

9

globals引数とlocals引数を指定しない場合

globals引数とlocals引数に値を渡さなかった場合、プログラムにインポートされたモジュールに基づくデフォルトで利用可能な関数がすべて使える状態になります。次の例では、両方の引数を省略したときに、利用可能なすべての関数が表示される様子を確認できます。

from time import *
exec("print(dir())")

上記のコードを実行すると、次のような結果が得られます(環境により内容は異なります)。

['__builtins__', '__doc__', '__name__', '__package__', 'asinh', 'atan', 'atan2', 'atanh',
'ceil', 'clock', 'copysign', 'cos', 'cosh', 'ctime', 'daylight', 'degrees', 'e', 'erf',
'erfc', 'exit', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum',
'gamma', 'gmtime', 'hypot', 'isinf', 'isnan', 'ldexp', 'lgamma', 'localtime', 'log',
'log10', 'log1p', 'mktime', 'modf', 'pi', 'pow', 'prog', 'prog_block', 'quit', 'radians',
'sin', 'sinh', 'sleep', 'sqrt', 'strftime', 'strptime', 'struct_time', 'tan', 'tanh',
'time', 'timezone', 'trunc', 'tzname', 'x']

globals引数でアクセスを制限する

globals引数として空の辞書を渡すことで、インポートしたモジュールの関数へのアクセスを制限できます。この場合、結果には組み込み関数のみが表示され、インポートしたモジュールの関数は表示されません。これにより、exec()の実行環境を制限し、より安全にすることができます。

from time import *
exec("print(dir())", {})

出力

上記のコードを実行すると、次の結果が得られます。

['__builtins__']

特定の関数だけを許可する

次に、インポートしたモジュールの中から選択した特定の関数だけをexec()内で使えるようにする方法を見てみましょう。次の例では、globals引数に必要な関数だけを指定して渡しています。localtime()関数は、プログラムにインポートされたtimeモジュールの一部です。

from time import *
exec("print(lclt())", {"lclt": localtime})

出力

上記のコードを実行すると、次の結果が得られます。

time.struct_time(tm_year=2019, tm_mon=7, tm_mday=19, tm_hour=12, tm_min=33, tm_sec=53, tm_wday=4, tm_yday=200, tm_isdst=0)

locals引数を渡してさらに制限する

locals引数を活用すれば、組み込み関数を完全に排除しつつ、インポートしたモジュールの各種関数の使用を制限することもできます。次の例では、組み込み関数を無効化するため、globals引数の__builtins__Noneを指定しています。

from time import *
exec("print(dir())", {"__builtins__": None}, {"gtime": gmtime, "print": print, "dir": dir})

上記のコードを実行すると、次の結果が得られます。

['dir', 'gtime', 'print']

このようにexec()関数は非常に強力ですが、任意のコードを実行できる分、使い方を誤るとセキュリティリスクにつながります。globals引数やlocals引数を適切に制限することで、安全に活用しましょう。

  1. Pythonコードを高速化する11の最適化テクニック

    Pythonはコンパイル言語ほど高速ではないことは周知の事実ですが、大手企業がPythonで大規模なワークロードを処理している事例からも分かるように、決して「遅い言語」ではありません。この記事では、正しく動作するPythonプログラムをさらに速く、効率的にするために押さえておきたい最適化のヒントを11個紹介します。 ヒント1: 組み込み関数を活用する Pythonで独自に効率的なコードを書くことも可能ですが、C言語で実装された組み込み関数の速度には到底及びません。利用可能な組み込み関数はdir(__builtins__)で一覧表示できるので、車輪の再発明をする前に必ず確認しましょう。 ヒン

  2. Pythonのインデントエラー(IndentationError)を修正する方法|原因と解決策を徹底解説

    Pythonとは Pythonは1991年に初めてリリースされたプログラミング言語です。大規模かつ充実したライブラリを備えており、関数型・命令型・手続き型・オブジェクト指向など、さまざまなプログラミングパラダイムをサポートしていることで知られています。 「IndentationError: Expected an indented block(インデントエラー:インデントされたブロックが必要です)」は、初心者から経験豊富な開発者まで、あらゆるユーザーに発生しうるエラーです。Pythonでは正しい空白(ホワイトスペース)によってコードの構造が定められているため、インデントが不正だとコードはコン