Pythonのeval()/exec()を使う前に知っておきたいこと
Pythonを使っていると、eval()
や exec()
という組込関数を見かけることがあります。これらは一見便利そうに見えますが、利用は非常に注意が必要な「切れ味の利刀」的な工具です。
eval() と exec() は何が違うのか
特徴 | eval() |
exec() |
---|---|---|
📚 用途 | 式 (expression)を評価 | 文 (statement)を実行 |
↺ 戻り値 | あり (expressionの結果) | なし (None) |
例 |
eval("2 + 3") → 5 |
exec("for i in range(3): print(i)") |
注意 | 安易に使わない | もっと注意 |
eval() の利用例
- ユーザーからの入力を式として評価したい場合などに便利
x = 10
eval("x + 5") # 結果: 15
exec() の利用例
- 複数行のコードや代入を実行したい場面で使われる
exec("""
for i in range(3):
print(i)
""")
# 結果:
# 0
# 1
# 2
eval/exec が何故実装されたのか
-
Pythonの言語設計思想
- Pythonは「動的」「封装性」を重視
- 式をデータとして扱いたいニーズに対応
-
REPL (対話式実行)システムの強化
- Pythonは初期からスクリプト言語を持つ
- 文字列で受け取った式/文をその場で評価・実行するため
-
テストツールやスクリプトの動作定義
- DSL、シミュレータ、Jupyterの内部など
eval/exec を使うべきでない理由
原因 | 説明 |
---|---|
セキュリティリスク | 任意のコードが実行される可能性 |
デバッグが難しい | 動的な評価のため追跡が困難 |
保守性が低い | 文字列の中身を理解するのが困難 |
代替手段とそのサンプルコード
🔁 比較式の評価 → operator
モジュール
import operator
ops = {
'>': operator.gt,
'<': operator.lt,
'==': operator.eq,
'>=': operator.ge,
'<=': operator.le,
'!=': operator.ne,
}
value = 120
threshold = 100
operator_str = '>'
if ops[operator_str](value, threshold):
print("条件を満たしました")
🔄 文字列をデータに変換 → ast.literal_eval()
import ast
safe_data = ast.literal_eval("[1, 2, 3]") # リストとして評価
print(safe_data) # → [1, 2, 3]
# 危険なコードは評価されない(例:__import__('os') など)
🔁 動的な切り替え → 関数/クラス/ディクショナリで対応
def process_add(x, y):
return x + y
def process_sub(x, y):
return x - y
operations = {
'add': process_add,
'sub': process_sub,
}
op_name = 'add'
result = operations[op_name](5, 3) # → 8
print(result)
おわりに: "力を与えるが責任も負わせる"
eval()
や exec()
は Python の「力」の表現です。しかし、正しく使われないなら、同時に「剣」にもなります。
何故あるのか、どんな場合なら許されるのかを理解した上で、避けられるなら代替手段を選んでいきましょう。