Динамическое создание и выполнение кода в Python

Раздел: Продвинутый Python -> Метапрограммирование

Метапрограммирование: работа с динамическим кодом

Как выполнить произвольный код, переданный в виде строки?

exec() позволяет выполнить блок кода Python из строки. Синтаксис: exec(code_string, globals, locals). Первый аргумент - строка кода. Дополнительные аргументы задают глобальное и локальное пространства имен.


code = 'x = 10\ny = x * 2\nprint(y)'
exec(code)  # Выведет 20
  

Python динамический код (динамический код python)

Если нужно получить переменные после exec, передайте словарь как locals:


scope = {}
exec('result = 42', scope)
print(scope['result'])  # 42
  

Типичные ошибки: SyntaxError при некорректном коде; случайное переопределение встроенных имен при использовании пустого globals. Для изоляции передавайте копию __builtins__.

Как вычислить значение выражения, полученного в виде строки?

eval() вычисляет одно выражение и возвращает результат. Пример:


expr = '2 + 3 * 4'
result = eval(expr)
print(result)  # 14
  

Для безопасности ограничьте доступные имена:


safe_dict = {'__builtins__': {}}
val = eval("__import__('os').system('ls')", safe_dict)  # Ошибка, нет __import__
  

Ошибки: NameError при обращении к неопределенной переменной. eval опасен, если выражение содержит вызовы функций с побочными эффектами.

Как подготовить код для многократного выполнения без повторного синтаксического анализа?

compile() переводит строку в объект кода. Затем exec выполнит этот объект.


code_str = 'for i in range(5): print(i)'
code_obj = compile(code_str, '<string>', 'exec')
exec(code_obj)
  

Можно скомпилировать в режиме 'eval' для выражения.

Ошибки: неправильный режим (single, exec, eval) вызывает ValueError. При повторном использовании необходимо следить за пространством имен.

Как создать новый класс без использования ключевого слова class?

type(name, bases, dict) создает класс динамически.


MyClass = type('MyClass', (object,), {'x': 10, 'method': lambda self: self.x})
obj = MyClass()
print(obj.method())  # 10
  

Методы можно передавать как функции.

Проблемы: сложность добавления наследования и магических методов. Ошибка при неверной сигнатуре метода (пропущен self).

Расширенные примеры динамического кода

Первый пример: создание и выполнение динамической функции с сохранением в пространстве имен.

Пример

import types
code = '''
def greet(name):
    return f'Hello, {name}!'
'''
namespace = {}
exec(code, namespace)
greet_func = namespace['greet']
print(greet_func('World'))
Hello, World!

Второй пример: безопасное вычисление математических выражений с помощью ast.literal_eval и проверки на вызовы функций.

Пример

import ast
def safe_eval(expr):
    try:
        tree = ast.parse(expr, mode='eval')
        for node in ast.walk(tree):
            if isinstance(node, ast.Call):
                raise ValueError('Function calls are not allowed')
        return ast.literal_eval(expr)
    except Exception as e:
        return f'Error: {e}'
print(safe_eval('2 + 3 * 4'))
expr = '__import__("os")'
print(safe_eval(expr))
14
Error: Function calls are not allowed

Динамический код Python - comments

En
Python динамический код (python)