Die Python-Funktion exec(code) führt einen String oder ein Code-Objekt als Python-Statements aus — anders als eval(), das nur Ausdrücke kennt. Damit lassen sich Funktionen, Klassen, Schleifen und mehr dynamisch generieren. Wie eval() ist exec() extrem mächtig — und extrem gefährlich, sobald die Eingabe nicht vertrauenswürdig ist.
Einleitung
exec() ist Pythons mächtigstes Code-Generierungs-Werkzeug. Anwendungsfälle sind:
- Code-Generatoren: Templates, ORM-Mapper, Test-Generatoren.
- Notebooks/REPLs: User-Eingabe als Code ausführen.
- DSLs: Eigene Mini-Sprachen, die in Python übersetzt werden.
Dabei gilt dasselbe wie für eval(): Niemals mit ungeprüftem Input. exec(user_input) ist ein offener Türgriff für Angreifer.
exec() läuft im Default in den aufrufenden Globals und Locals — Variablen, die im exec'd Code definiert werden, landen also dort. Mit explizitem globals-Dict lässt sich der Scope abschotten.
Syntax
exec(code, globals=None, locals=None)code Ein String mit Python-Code oder ein per compile(..., mode="exec") vorbereitetes Code-Objekt.
globals (Optional) Dict mit globalen Variablen. Default: aktuelle Globals.
locals (Optional) Dict mit lokalen Variablen.
Rückgabewert
Immer None. Im Gegensatz zu eval() gibt exec() nie ein Ergebnis zurück — Effekte landen in den übergebenen globals/locals.
Beispiele
Statement ausführen
exec() kann mehrere Statements auf einmal verarbeiten — perfekt für mehrzeiligen generierten Code:
code = """
for i in range(3):
print(f"Schleife {i}")
"""
exec(code)Schleife 0
Schleife 1
Schleife 2Funktion dynamisch erzeugen
Mit exec() lässt sich eine Funktion zur Laufzeit aus einem String erzeugen — die Definition landet im übergebenen locals-Dict:
ns = {}
exec("def double(x): return x * 2", ns)
print(ns["double"](21))42In abgeschottetem Namespace
Ein eigener globals-Dict isoliert den ausgeführten Code vom Rest des Programms:
sandbox = {"__builtins__": {"print": print}}
exec("print('läuft im sandbox')", sandbox)
# Versuch zu importieren schlägt fehl:
try:
exec("import os", sandbox)
except ImportError as e:
print("Blockiert:", e)läuft im sandbox
Blockiert: __import__ not foundPraktische Beispiele
Klasse aus Template generieren
dataclasses machen das ähnlich intern — sie bauen __init__ aus einer Klassen-Definition zur Laufzeit:
fields = ["name", "age", "city"]
params = ", ".join(fields)
body = "\n ".join(f"self.{f} = {f}" for f in fields)
template = f"""
class User:
def __init__(self, {params}):
{body}
def __repr__(self):
return f"User({{', '.join(f'{{k}}={{getattr(self, k)!r}}' for k in {fields!r})}})"
"""
ns = {}
exec(template, ns)
u = ns["User"]("Michael", 34, "Berlin")
print(u.name, u.age, u.city)Michael 34 BerlinCompile + exec für mehrfaches Ausführen
Wenn derselbe Code-String mehrmals ausgeführt wird, lohnt sich compile() — der Parsing-Overhead fällt nur einmal an:
code = compile("total += value", "<input>", "exec")
ns = {"total": 0}
for value in [1, 2, 3, 4, 5]:
ns["value"] = value
exec(code, ns)
print(ns["total"])15Praktische Hinweise
- Niemals mit User-Input ohne intensive Validierung — Code-Injection ist die Folge.
- In Funktionen ist
exec()tricky — Schreib-Effekte auf lokale Variablen sind ohne expliziteslocals-Dict nicht zuverlässig. - Alternative: Ein
dict-basiertes Konfig-Format,ast.literal_evalfür Daten oderimportlibfür dynamisches Modul-Loading. - Template-Engines (
Jinja2,Mako) sind die richtige Wahl für Text-Generierung —exec()für echten Python-Code. - Verwandte Funktionen:
eval()(Ausdrücke),compile(),importlib.import_module(),types.FunctionType.