2.34. Cod dinamic¶
Trei funcții încorporate primesc un șir de cod sursă Python și îl rulează: eval(), exec() și compile(). Împreună, ele permit codului să construiască și să execute mai mult cod în timpul execuției – ceea ce este uneori exact instrumentul potrivit și, mult mai des, o sursă de erori și breșe de securitate.
Atenționare
Aceste funcții execută cod Python arbitrar. Transmiterea de date introduse de utilizator către eval sau exec – un șir dintr-un fișier pe care utilizatorul îl poate edita, o încărcătură primită prin rețea, o valoare tastată la un prompt REPL – permite acelei intrări să facă orice ar putea face scriptul apelant, inclusiv ștergerea fiecărui fișier de pe dispozitiv. Folosește-le deliberat, niciodată în cod care rulează automat, și niciodată pe date pe care nu le controlezi.
2.34.1. eval¶
eval() rulează o singură expresie și întoarce valoarea ei:
>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'
Expresia vede implicit variabilele globale și locale ale apelantului, motiv pentru care name se rezolvă în al doilea exemplu. Transmiterea de dicționare explicite îți permite să izolezi evaluarea într-un mediu controlat:
eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})
Chiar și izolat, eval este periculos. Există tehnici binecunoscute de a evada din astfel de medii controlate; nu te baza doar pe trucul __builtins__ gol pentru date nesigure.
2.34.2. exec¶
exec() rulează un bloc de cod în loc de o singură expresie – instrucțiuni, definiții de funcții, bucle – și întoarce None:
exec("for i in range(3): print(i)")
Rezultat:
0
1
2
Blocul poate defini nume care devin disponibile ulterior, cu unele rezerve privind domeniul local față de cel global. exec în interiorul unei funcții se comportă rareori așa cum se așteaptă autorul; dacă ai nevoie de el, rulează-l la nivel de modul.
2.34.3. compile¶
compile() transformă un șir sursă într-un obiect cod care poate fi transmis ulterior către eval() sau exec(). Folosește-l când aceeași sursă va rula de multe ori – analiza se face o dată, execuția este mai rapidă:
expr = compile("x * x", "<expr>", "eval")
for x in range(5):
print(eval(expr))
Rezultat:
0
1
4
9
16
Argumentul din mijloc este o etichetă care apare în trasările de stivă dacă codul ridică o eroare. Al treilea argument este "eval" pentru o singură expresie, "exec" pentru un bloc sau "single" pentru o instrucțiune de tip interactiv care își afișează rezultatul.
2.34.4. Când să apelezi la acestea¶
Aproape niciodată. Majoritatea cazurilor de utilizare pe care le imaginează începătorii au alternative mai sigure:
Citirea unui fișier de configurare. Folosește
json– date structurate, fără execuție.Evaluarea unei valori numerice tastate de utilizator. Folosește
int()/float()pentru analiză, apoi aritmetică. Dacă utilizatorul chiar trebuie să introducă o formulă, folosește un mic analizor de expresii, nueval.
Când chiar ai nevoie de eval / exec / compile, izolează locul apelului, înregistrează șirul exact care urmează să fie executat și tratează sursa ca fiind cel mai suspect lucru din codul tău.