2.34. Dynamisk kod

Tre inbyggda funktioner tar en sträng med Python-källkod och kör den: eval(), exec() och compile(). Tillsammans låter de kod konstruera och köra mer kod vid körtid – vilket ibland är exakt rätt verktyg, och betydligt oftare en källa till buggar och säkerhetshål.

Varning

Dessa funktioner kör godtycklig Python. Att skicka användarinmatning till eval eller exec – en sträng från en fil som användaren kan redigera, en nyttolast som tas emot över nätverket, ett värde inskrivet vid en REPL-prompt – låter den inmatningen göra allt som det anropande skriptet kan göra, ända upp till och inklusive att radera varje fil på enheten. Använd dem medvetet, aldrig inuti kod som körs automatiskt, och aldrig på data som du inte kontrollerar.

2.34.1. eval

eval() kör ett enda uttryck och returnerar dess värde:

>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'

Uttrycket ser anroparens globala och lokala namn som standard, vilket är anledningen till att name löses upp i det andra exemplet. Att skicka explicita ordböcker låter dig sandlådebegränsa utvärderingen:

eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})

Även sandlådebegränsat är eval farligt. Det finns välkända tekniker för att fly från sådana sandlådor; förlita dig inte på enbart det tomma __builtins__-tricket för opålitlig inmatning.

2.34.2. exec

exec() kör ett block med kod snarare än ett enda uttryck – satser, funktionsdefinitioner, loopar – och returnerar None:

exec("for i in range(3): print(i)")

Utdata:

0
1
2

Blocket kan definiera namn som blir tillgängliga efteråt, med vissa förbehåll om lokalt kontra globalt omfång. exec inuti en funktion beter sig sällan på det sätt som skribenten förväntar sig; om du behöver det, kör det på modulnivå.

2.34.3. compile

compile() förvandlar en källkodssträng till ett kodobjekt som kan skickas till eval() eller exec() senare. Använd det när samma källkod ska köras många gånger – tolkningen sker en gång, exekveringen blir snabbare:

expr = compile("x * x", "<expr>", "eval")
for x in range(5):
    print(eval(expr))

Utdata:

0
1
4
9
16

Det mellersta argumentet är en etikett som visas i tracebacks om koden höjer ett fel. Det tredje argumentet är "eval" för ett enda uttryck, "exec" för ett block, eller "single" för en interaktiv sats som skriver ut sitt resultat.

2.34.4. När du bör ta till dessa

Nästan aldrig. De flesta användningsfall som nybörjare föreställer sig har säkrare alternativ:

  • Läsa en konfigurationsfil. Använd json – strukturerad data, ingen exekvering.

  • Utvärdera ett numeriskt värde inskrivet av användaren. Använd int() / float() för att tolka, sedan aritmetik. Om användaren verkligen behöver mata in en formel, använd en liten uttryckstolk, inte eval.

När du verkligen behöver eval / exec / compile, isolera anropsstället, logga den exakta strängen som är på väg att köras, och behandla källkoden som det mest misstänkta i din kod.