2.34. Dynaaminen koodi¶
Kolme sisäänrakennettua funktiota ottaa merkkijonon Python-lähdekoodia ja suorittaa sen: eval(), exec() ja compile(). Yhdessä ne antavat koodin rakentaa ja suorittaa lisää koodia ajonaikaisesti – mikä on toisinaan juuri oikea työkalu ja paljon useammin bugien ja tietoturva-aukkojen lähde.
Varoitus
Nämä funktiot suorittavat mielivaltaista Pythonia. Käyttäjän syötteen välittäminen funktiolle eval tai exec – merkkijono käyttäjän muokattavissa olevasta tiedostosta, verkon yli vastaanotettu kuorma, REPL-kehotteeseen kirjoitettu arvo – antaa tuon syötteen tehdä mitä tahansa, mitä kutsuva skripti voisi tehdä, aina jokaisen laitteella olevan tiedoston poistamiseen asti. Käytä niitä harkiten, älä koskaan koodissa, joka suoritetaan automaattisesti, äläkä koskaan datalla, jota et hallitse.
2.34.1. eval¶
eval() suorittaa yksittäisen lausekkeen ja palauttaa sen arvon:
>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'
Lauseke näkee oletuksena kutsujan globaalit ja paikalliset muuttujat, minkä vuoksi name selviää toisessa esimerkissä. Eksplisiittisten sanakirjojen välittäminen antaa sinun hiekkalaatikoida evaluoinnin:
eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})
Hiekkalaatikoitunakin eval on vaarallinen. On olemassa tunnettuja tekniikoita tällaisista hiekkalaatikoista pakenemiseen; älä luota pelkkään tyhjän __builtins__ -temppuun epäluotettavan syötteen kanssa.
2.34.2. exec¶
exec() suorittaa koodilohkon yksittäisen lausekkeen sijaan – lauseita, funktiomäärittelyjä, silmukoita – ja palauttaa arvon None:
exec("for i in range(3): print(i)")
Tuloste:
0
1
2
Lohko voi määritellä nimiä, jotka tulevat käytettäviksi jälkikäteen, joitakin varauksia paikallisesta ja globaalista näkyvyysalueesta lukuun ottamatta. exec funktion sisällä käyttäytyy harvoin niin kuin kirjoittaja olettaa; jos tarvitset sitä, suorita se moduulitasolla.
2.34.3. compile¶
compile() muuntaa lähdekoodimerkkijonon koodiolioksi, jonka voi välittää myöhemmin funktiolle eval() tai exec(). Käytä sitä, kun sama lähdekoodi suoritetaan monta kertaa – jäsentäminen tapahtuu kerran, suoritus on nopeampi:
expr = compile("x * x", "<expr>", "eval")
for x in range(5):
print(eval(expr))
Tuloste:
0
1
4
9
16
Keskimmäinen argumentti on nimike, joka näkyy jäljityksissä, jos koodi nostaa poikkeuksen. Kolmas argumentti on "eval" yksittäiselle lausekkeelle, "exec" lohkolle tai "single" interaktiivistyyliselle lauseelle, joka tulostaa tuloksensa.
2.34.4. Milloin näihin kannattaa tarttua¶
Lähes ei koskaan. Useimmilla käyttötapauksilla, joita aloittelijat kuvittelevat, on turvallisempia vaihtoehtoja:
Asetustiedoston lukeminen. Käytä moduulia
json– rakenteista dataa, ei suoritusta.Käyttäjän kirjoittaman numeerisen arvon evaluointi. Käytä funktiota
int()/float()jäsentämiseen ja sitten laskutoimituksia. Jos käyttäjän todella täytyy syöttää kaava, käytä pientä lausekejäsennintä, ei funktiotaeval.
Kun todella tarvitset funktiota eval / exec / compile, eristä kutsupaikka, kirjaa lokiin tarkka merkkijono, joka on kohta suoritettavana, ja kohtele lähdekoodia koodisi epäilyttävimpänä asiana.