Я хочу назначить переменные Python, импортированные из файла JSON. На этот вопрос был интересный ответ с использованием classmethod, но я не смог заставить его работать, а комментировать мне запрещено....
Итак, давайте рассмотрим очень простой пример: Я хочу оценить z = x^2+y^2, но я хочу иметь возможность определить x и y в JSON-файле. Мой json-файл (params.json
) может выглядеть следующим образом:
{
"x":3,
"y":2
}
Затем я мог бы загрузить файл и сгенерировать динамические переменные:
with open("params.json", "r") as read_file:
params = json.load(read_file)
for k, v in params.items():
vars()[k] = v
z = x^2+y^2
Это работает, но кажется опасным динамически генерировать переменные. Есть ли стандартный/более разумный способ сделать это?
Это во многом зависит от ваших целей безопасности и от того, какой пользовательский интерфейс вы хотите предложить автору этих выражений.
Загрузка переменных в локальную область работает, поскольку Python — очень динамичный язык. Однако существует риск того, что переменные могут переопределить существующие объекты, что нарушит ваш код — что, если, например, есть переменная с именем len
?
Поэтому обычно безопаснее избегать запуска пользовательского ввода в контексте Python. Вместо:
У Python есть инструменты, которые помогут в этом. Мы можем анализировать строки как код Python с помощью модуля ast. Это возвращает структуру данных, представляющую синтаксис, и ничего не выполняет (хотя синтаксический анализатор не обязательно защищен от вредоносных входных данных). Мы можем взять структуру данных, пройтись по ней и выполнить ее в соответствии с определенными нами правилами, например, разрешая переменные только из словаря. Пример кода для Python 3.10:
import ast
def interpret(code: str, variables: dict) -> dict:
module: ast.Module = ast.parse(code, mode='exec')
for statement in module.body:
_interpret_statement(statement, variables)
return variables
def _interpret_statement(statement: ast.stmt, variables: dict) -> None:
match statement:
case ast.Assign(targets=[ast.Name(id=name)], value=value):
variables[name] = _interpret_expr(value, variables)
return
case other:
raise InterpreterError("Syntax not supported", other)
def _interpret_expr(expr: ast.expr, variables: dict) -> Any:
match expr:
case ast.BinOp(left=left_ast, op=op, right=right_ast):
left = _interpret_expr(left_ast, variables)
right = _interpret_expr(right_ast, variables)
return _interpret_binop(left, op, right)
case ast.Name(id=name):
return variables[name]
case ast.Constant(value=(int(value) | float(value))):
return value
case other:
raise InterpreterError("Syntax not supported", other)
def _interpret_binop(left: Any, op: ast.operator, right: Any) -> Any:
match op:
case ast.Add(): return left + right
case ast.Sub(): return left - right
case ast.Mult(): return left * right
case ast.Div(): return left / right
case ast.Pow(): return left**right
case other:
raise InterpreterError(
"Operator not supported",
ast.BinOp(ast.Name("_"), other, ast.Name("_")))
class InterpreterError(Exception):
def __init__(self, msg: str, code: Optional[ast.AST] = None) -> None:
super().__init__(msg, code)
self._msg = msg
self._code = code
def __str__(self):
if self._code:
return f"{self._msg}: {ast.unparse(self._code)}"
return self._msg
Затем это можно использовать для интерпретации команд, возвращая словарь со всеми переменными:
>>> interpret("z = x**2+y**2", {"x": 3, "y": 2})
{'x': 3, 'y': 2, 'z': 13}
Хотя это позволяет вам интерпретировать код Python так, как вы хотите (вы контролируете семантику), вы по-прежнему ограничены синтаксисом Python. Например, вы должны использовать оператор **
для возведения в степень, а не ^
xor-оператор Python.
Если вам нужен собственный синтаксис, вам, вероятно, придется написать собственный синтаксический анализатор. Существует множество алгоритмов синтаксического анализа и генераторов синтаксических анализаторов, но я неравнодушен к написанному от руки «рекурсивному спуску». Обычно это включает в себя написание рекурсивных функций вида parse(Position) -> Optional[tuple[Position, Value]]
, которые постепенно потребляют ввод. Я написал пример синтаксического анализатора и интерпретатора , используя эту стратегию, и ранее сравнивал различные подходы к синтаксическому анализу в ответе о реализации языков запросов в программе Python.