Come eseguo una stringa contenente il codice Python in Python?
Come eseguo una stringa contenente il codice Python in Python?
Risposte:
Per le dichiarazioni, utilizzare exec(string)
(Python 2/3) o exec string
(Python 2):
>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world
Quando è necessario il valore di un'espressione, utilizzare eval(string)
:
>>> x = eval("2+2")
>>> x
4
Tuttavia, il primo passo dovrebbe essere quello di chiederti se è davvero necessario. L'esecuzione del codice dovrebbe generalmente essere la posizione dell'ultima risorsa: è lenta, brutta e pericolosa se può contenere il codice inserito dall'utente. Dovresti sempre esaminare prima le alternative, come le funzioni di ordine superiore, per vedere se possono soddisfare meglio le tue esigenze.
if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42
etc, che possono quindi scrivere come exec ("x.%s = 42" % s)
. Per questo caso comune (in cui è necessario solo accedere all'attributo di un oggetto memorizzato in una stringa), esiste una funzione molto più veloce, più pulita e più sicura getattr
: basta scrivere getattr(x, s) = 42
per significare la stessa cosa.
setattr(x, s, 42)
? Ci ho provato getattr(x, 2) = 42
e non ci sono riuscitocan't assign to function call: <string>, line 1
setattr(x, s, 42)
è la sintassi giusta. Sorpreso, ci è voluto così tanto tempo per rilevare quell'errore. Comunque, il punto è questo getattr
e setattr
sono un'alternativa a exec
quando tutto ciò che vuoi è ottenere un membro arbitrario, cercato da stringa.
Nell'esempio una stringa viene eseguita come codice usando la funzione exec.
import sys
import StringIO
# create file-like string to capture output
codeOut = StringIO.StringIO()
codeErr = StringIO.StringIO()
code = """
def f(x):
x = x + 1
return x
print 'This is my output.'
"""
# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr
exec code
# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
print f(4)
s = codeErr.getvalue()
print "error:\n%s\n" % s
s = codeOut.getvalue()
print "output:\n%s" % s
codeOut.close()
codeErr.close()
exec
(a meno che tu non sappia che la stringa di codice proviene da una fonte attendibile).
eval
e exec
sono la soluzione corretta e possono essere utilizzati in modo più sicuro modo .
Come discusso nel manuale di riferimento di Python e chiaramente spiegato in questo tutorial, il eval
eexec
funzioni accettano due parametri extra che consentono all'utente di specificare quali funzioni e variabili globali e locali sono disponibili.
Per esempio:
public_variable = 10
private_variable = 2
def public_function():
return "public information"
def private_function():
return "super sensitive information"
# make a list of safe functions
safe_list = ['public_variable', 'public_function']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# add any needed builtins back in
safe_dict['len'] = len
>>> eval("public_variable+2", {"__builtins__" : None }, safe_dict)
12
>>> eval("private_variable+2", {"__builtins__" : None }, safe_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'private_variable' is not defined
>>> exec("print \"'%s' has %i characters\" % (public_function(), len(public_function()))", {"__builtins__" : None}, safe_dict)
'public information' has 18 characters
>>> exec("print \"'%s' has %i characters\" % (private_function(), len(private_function()))", {"__builtins__" : None}, safe_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'private_function' is not defined
In sostanza si sta definendo lo spazio dei nomi in cui verrà eseguito il codice.
eval
o intendi exec
essere usato come exec(input("Type what you want"))
? Ci sono molti casi in cui un programma può scrivere una procedura o una funzione a seguito di un calcolo; le funzioni risultanti saranno sicure e veloci (una volta valutate) come qualsiasi altra parte di un programma valido e ben scritto. Un programma non sicuro contenente exec
non è più pericoloso di un programma non sicuro che fa il danno da solo in quanto exec
non dà alcun nuovo privilegio al programma.
exec
eeval
exec
e eval
in Python è fortemente disapprovato.Dalla prima risposta (enfasi sulla mia):
Per le dichiarazioni, utilizzare
exec
.Quando hai bisogno del valore di un'espressione, usa
eval
.Tuttavia, il primo passo dovrebbe essere quello di chiederti se è davvero necessario. L'esecuzione del codice dovrebbe generalmente essere la posizione dell'ultima risorsa : è lenta, brutta e pericolosa se può contenere il codice inserito dall'utente. Dovresti sempre esaminare prima le alternative, come le funzioni di ordine superiore , per vedere se possono soddisfare meglio le tue esigenze.
Dalle alternative a exec / eval?
impostare e ottenere valori di variabili con i nomi nelle stringhe
[mentre
eval
] funzionerebbe, in genere non è consigliabile utilizzare nomi di variabili che abbiano un significato per il programma stesso.Invece, meglio usare un dict.
Da http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (sottolineatura mia)
Python non è PHP
Non cercare di eludere i modi di dire di Python perché qualche altra lingua lo fa diversamente. Gli spazi dei nomi sono in Python per un motivo e solo perché ti dà lo strumento
exec
non significa che dovresti usare quello strumento.
Da http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (sottolineatura mia)
Quindi eval non è sicuro, anche se rimuovi tutti i globi e i builtin!
Il problema con tutti questi tentativi di proteggere eval () è che sono liste nere . Rimuovono esplicitamente cose che potrebbero essere pericolose. Questa è una battaglia persa perché se c'è solo un oggetto lasciato dalla lista, puoi attaccare il sistema .
Quindi, eval può essere reso sicuro? Difficile da dire. A questo punto, la mia ipotesi migliore è che non puoi fare alcun male se non puoi usare doppi trattini bassi, quindi forse se escludi una stringa con doppi trattini bassi sei al sicuro. Può essere...
Da http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (sottolineatura mia):
Innanzitutto,
exec
rende più difficile per gli esseri umani leggere il tuo codice . Per capire cosa sta succedendo, non devo solo leggere il tuo codice, devo leggere il tuo codice, capire quale stringa genererà, quindi leggere quel codice virtuale. Quindi, se stai lavorando in un team o pubblichi software open source o chiedi aiuto da qualche parte come StackOverflow, stai rendendo più difficile per gli altri aiutarti. E se c'è qualche possibilità che tu stia eseguendo il debug o espandendo questo codice tra 6 mesi, lo stai rendendo più difficile direttamente per te.
cfg.yaml
):, reldir : ../my/dir/
e reldir = cfg[reldir]
. Tuttavia, poiché questo codice Python deve essere eseguito su Windows e Linux, ho bisogno di questo per adattarmi ai diversi divisori di percorso dei sistemi operativi; o \\
oppure /
. Quindi uso reldir : os.path.join('..','my','dir')
nel file di configurazione. Ma questo risulta solo reldir
essere questa stringa letterale, non essere valutata, quindi non posso aprire un file reldir
. Hai un suggerimento?
Si esegue l'esecuzione del codice utilizzando exec, come con la seguente sessione IDLE:
>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']
4
Come menzionato dagli altri, è "exec" ..
ma, nel caso in cui il codice contenga variabili, è possibile utilizzare "global" per accedervi, anche per impedire al compilatore di generare il seguente errore:
NameError: il nome 'p_variable' non è definito
exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
Vale la pena ricordare che quel exec
fratello esiste così chiamato execfile
se vuoi chiamare un file Python. Questo a volte va bene se stai lavorando in un pacchetto di terze parti che include terribili IDE e vuoi codificare al di fuori del loro pacchetto.
Esempio:
execfile('/path/to/source.py)'
o:
exec(open("/path/to/source.py").read())
Ho provato alcune cose, ma l'unica cosa che ha funzionato è stata la seguente:
temp_dict = {}
exec("temp_dict['val'] = 10")
print(temp_dict['val'])
produzione:
10
La soluzione più logica sarebbe quella di usare la funzione eval () integrata. Un'altra soluzione è scrivere quella stringa in un file Python temporaneo ed eseguirla.
Ok .. So che questa non è esattamente una risposta, ma forse una nota per le persone che la guardano così com'ero. Volevo eseguire un codice specifico per diversi utenti / clienti, ma volevo anche evitare exec / eval. Inizialmente ho cercato di archiviare il codice in un database per ciascun utente e di fare quanto sopra.
Ho finito per creare i file sul file system all'interno di una cartella 'customer_filters' e usando il modulo 'imp', se nessun filtro è stato applicato per quel cliente, è andato avanti
import imp
def get_customer_module(customerName='default', name='filter'):
lm = None
try:
module_name = customerName+"_"+name;
m = imp.find_module(module_name, ['customer_filters'])
lm = imp.load_module(module_name, m[0], m[1], m[2])
except:
''
#ignore, if no module is found,
return lm
m = get_customer_module(customerName, "filter")
if m is not None:
m.apply_address_filter(myobj)
così customerName = "jj" eseguirà apply_address_filter dal file customer_filters \ jj_filter.py