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

Pythonのeval()関数とは?基本構文からglobals・localsの使い方まで徹底解説

Pythonのeval()関数は、文字列として渡された式をPythonの式として解析(パース)し、プログラム内で実際に実行するための組み込み関数です。文字列で表現された数式やコードを動的に評価できるため、電卓アプリや数式処理ツールなどで活用されることがあります。

eval()の基本構文

eval()関数の一般的な構文は以下の通りです。

eval(expression, globals=None, locals=None)

各引数の意味

  • expression:必須の文字列引数。ここには、Pythonの式として解析・実行したい文字列を指定します。

  • globals:省略可能な引数。辞書型で指定し、eval()内で利用できる変数や関数を制御します。明示的に指定しない場合、標準の組み込み名前空間が使用されます。

  • locals:省略可能な引数。eval()関数内で参照できるローカル変数やローカル関数を指定します。

それでは、具体的な例を見ながらeval()の動作を理解していきましょう。

シンプルなeval()の使用例

str1 = 'Hello'
print(eval('str1 == "Hello"'))
print(eval('str1 + ", Python"'))

実行結果

True
Hello, Python

このように、比較式や文字列連結などのPython式を文字列として渡すと、eval()がその場で評価して結果を返します。

ユーザー入力をeval()で評価する例

mathモジュールの関数を組み合わせると、ユーザーが入力した数式をそのまま計算できます。

from math import *
for l in range(1,3):
    func = input("Enter Math Function to Evaluate:\n")
    try:
        print(eval(func))
    except Exception as ex:
        print(ex)
        break
print('Done')

実行結果

Enter Math Function to Evaluate:
abs(-42)
42
Enter Math Function to Evaluate:
max(12, 3, 4 , 2, 17)
17
Done

入力された「abs(-42)」や「max(12, 3, 4, 2, 17)」といった文字列が、そのままPythonの式として評価され、計算結果が出力されています。

globalsとlocalsによる名前空間の確認

eval()にどのような関数や変数を渡せるかを理解するために、まず現在のグローバルスコープとローカルスコープに存在するシンボルの一覧を確認してみましょう。

from math import *
def square_root(n):
    return sqrt(n)
# 現在のグローバルシンボルテーブルを辞書として表示
print('List of Global symbols:\n',globals())
# 現在のローカルシンボルテーブルを辞書として表示
print('\nList of Local symbols:\n',locals())
# 現在のローカルスコープ内の名前一覧を表示
print('\nList of names in current local scope:\n',dir())

実行結果

List of Global symbols:
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None, '__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'acos': <built-in function acos>, 'acosh': <built-in function acosh>,
'asin': <built-in function asin>, 'asinh': <built-in function asinh>,
'atan': <built-in function atan>, 'atan2': <built-in function atan2>,
'atanh': <built-in function atanh>, 'ceil': <built-in function ceil>,
'copysign': <built-in function copysign>, 'cos': <built-in function cos>,
'cosh': <built-in function cosh>, 'degrees': <built-in function degrees>,
'erf': <built-in function erf>, 'erfc': <built-in function erfc>,
'exp': <built-in function exp>, 'expm1': <built-in function expm1>,
'fabs': <built-in function fabs>, 'factorial': <built-in function factorial>,
'floor': <built-in function floor>, 'fmod': <built-in function fmod>,
'frexp': <built-in function frexp>, 'fsum': <built-in function fsum>,
'gamma': <built-in function gamma>, 'gcd': <built-in function gcd>,
'hypot': <built-in function hypot>, 'isclose': <built-in function isclose>,
'isfinite': <built-in function isfinite>, 'isinf': <built-in function isinf>,
'isnan': <built-in function isnan>, 'ldexp': <built-in function ldexp>,
'lgamma': <built-in function lgamma>, 'log': <built-in function log>,
'log1p': <built-in function log1p>, 'log10': <built-in function log10>,
'log2': <built-in function log2>, 'modf': <built-in function modf>,
'pow': <built-in function pow>, 'radians': <built-in function radians>,
'sin': <built-in function sin>, 'sinh': <built-in function sinh>,
'sqrt': <built-in function sqrt>, 'tan': <built-in function tan>,
'tanh': <built-in function tanh>, 'trunc': <built-in function trunc>,
'pi': 3.141592653589793, 'e': 2.718281828459045,
'tau': 6.283185307179586, 'inf': inf, 'nan': nan,
'square_root': <function square_root at 0x055246F0>
}

List of names in current local scope:
['__annotations__', '__builtins__', '__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', 'sin', 'sinh', 'sqrt',
'square_root', 'tan', 'tanh', 'tau', 'trunc']

この出力から、from math import *によってmathモジュールの全関数がグローバルスコープに取り込まれていることが分かります。

globalsに空の辞書を渡してアクセスを制限する

globals引数に空の辞書を指定すると、eval()内からアクセスできる名前を大きく制限できます。

>>> print(eval('dir()', {}))
['__builtins__']

空の辞書を渡した場合でも、Pythonは自動的に__builtins__のみを提供します。つまり、通常の組み込み関数へのアクセスも遮断されることになります。

特定の関数だけを許可する安全なeval()の使い方

次の例では、mathモジュールの中でもabs()とpow()の2つの関数だけを使えるように制限しています。

for l in range(1, 4):
    func = input("Enter Math Function to Evaluate.\nAllowed Functions are: abs(x) and pow(x,y):\n")
    try:
        print(eval(func, {'absolute': abs, 'pow': pow}))
    except Exception as ex:
        print(ex)
        break
print('Done')

実行結果

Enter Math Function to Evaluate.
Allowed Functions are: abs(x) and pow(x,y):
abs(-74)
74
Enter Math Function to Evaluate.
Allowed Functions are: abs(x) and pow(x,y):
pow(2,7)
128.0
Enter Math Function to Evaluate.
Allowed Functions are: abs(x) and pow(x,y):
sqrt(47)
name 'sqrt' is not defined
Done

許可されていないsqrt(47)を実行しようとした際、「name 'sqrt' is not defined」というエラーが発生し、意図しない関数の実行を防げていることが確認できます。

eval()を使用する際のセキュリティ上の注意点

eval()は非常に便利な一方で、信頼できない入力に対して使用すると重大なセキュリティリスクをもたらします。悪意のあるユーザーが任意のコードを実行できてしまう可能性があるためです。

安全に使うためのポイントは以下の通りです。

  • ユーザー入力を直接eval()に渡さない
  • globals引数で実行可能な関数・変数を最小限に制限する
  • どうしても動的評価が必要な場合は、ast.literal_eval()などより安全な代替手段を検討する

これらの対策を講じた上で、eval()の強力な動的評価機能を活用しましょう。

  1. Pythonのissubset()関数とは?部分集合の判定方法を実例付きで解説

    本記事では、Python標準ライブラリに用意されているissubset()関数の仕組みと使い方について詳しく解説します。 issubset()メソッドは、あるセット(集合)のすべての要素が、引数として渡した別のセットにも含まれている場合にブール値のTrueを返し、1つでも含まれていない要素があればFalseを返します。 下の図では、BはAの部分集合です。もしAとBが同一のセットであれば、両者は互いに部分集合の関係にあるといえます。つまり、両方のセットがまったく同じ要素を持っているということを意味します。 構文 <set 1>.issubset(<set 2>) 戻り値

  2. Pythonのintersection()関数とは?集合の共通要素(積集合)を求める方法

    この記事では、Pythonのセット(集合)に対して実行できるintersection()関数について詳しく解説します。数学における「積集合(インターセクション)」とは、2つの集合から共通する要素を見つけ出すことを指します。構文<セット名>.intersection(<セットa1>, <セットa2>, …)戻り値引数として渡されたすべての集合に共通する要素(積集合)が、新しいセットとして返されます。使用例set_1 = {t,u,t,o,r,i,a,l} set_2 = {p,o,i,n,t} set_3 = {t,u,t} # 2つの集合の積集合 print