Come eseguo una stringa contenente il codice Python in Python?


357

Come eseguo una stringa contenente il codice Python in Python?


5
Ad essere onesti, a differenza dell'altra domanda, questo non è un duplicato. E altri hanno pubblicato domande molto più basilari di questa.
DNS

8
La risposta corretta, ovviamente, è quasi sempre "non farlo!".
Bobince,

23
@S. Lott: Non hai letto le FAQ di recente? "È anche perfetto porre e rispondere alla tua domanda di programmazione, ma fingi di essere su Jeopardy: esprimilo sotto forma di una domanda". Questa non è una cattiva domanda. +1
Devin Jeanpierre,

49
@ S.Lott: No. Non è necessario. Se la domanda non è già sul sito, allora è un gioco equo (secondo le FAQ, come già sottolineato). Rispondi a ogni domanda come se il PO avesse bisogno di aiuto. Forse no, ma probabilmente il prossimo che leggerà la loro domanda. Solo i miei 2 centesimi.
Bill the Lizard,

1
A tutti quelli che dicono di non farlo: ho un caso in cui ne ho assolutamente bisogno. Implica l'elaborazione distribuita.
sudo,

Risposte:


331

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.


1
ma che dire dell'ambito del codice eseguito da 'exec'? è nidificato?
jondinham,

21
Un caso comune in cui qualcuno vuole usare 'exec' è qualcosa come if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42etc, 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) = 42per significare la stessa cosa.
ShreevatsaR

5
In che modo exec è più lento dell'interprete Python?
Cris Stringfellow,

6
@ShreevatsaR non intendi setattr(x, s, 42)? Ci ho provato getattr(x, 2) = 42e non ci sono riuscitocan't assign to function call: <string>, line 1
Tanner Semerad l'

6
@Tanner: Hmm. Sì, in effetti setattr(x, s, 42)è la sintassi giusta. Sorpreso, ci è voluto così tanto tempo per rilevare quell'errore. Comunque, il punto è questo getattre setattrsono un'alternativa a execquando tutto ciò che vuoi è ottenere un membro arbitrario, cercato da stringa.
ShreevatsaR,

68

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()

1
scambiare stdout e stderr in quel modo mi rende molto nervoso. sembra che potrebbe causare enormi problemi di sicurezza. c'è un modo per aggirare questo?
Narcolapser,

1
@Narcolapser dovresti essere più interessato all'utilizzo di mych exec(a meno che tu non sappia che la stringa di codice proviene da una fonte attendibile).
bruno desthuilliers,

27

evale execsono 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 evaleexec 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.


9
Non è possibile rendere sicuro Eval : Eval è davvero pericoloso . Se prendi il codice da me e lo valuti, posso segfault il tuo programma Python. Gioco finito.
Ned Batchelder,

3
@ v.oddou stavo rispondendo alla dichiarazione di Alan, "eval ed exec .. possono essere usati in modo sicuro". Questo è falso Se qualcuno dicesse "bash può essere usato in modo sicuro" sarebbe anche falso. Bash è pericoloso. È un pericolo necessario, ma pericoloso comunque. Affermare che eval può essere reso sicuro è semplicemente sbagliato.
Ned Batchelder,

1
@NedBatchelder sì davvero. e il link che indichi è un buon materiale. dal potere deriva la responsabilità, quindi il punto è semplicemente essere consapevoli del potenziale potere di valutazione. e se decidiamo che potere = pericolo.
v

3
@NedBatchelder Anche molti pezzi di codice scritti in Python possono essere pericolosi, ma perché lo stai assumendo evalo intendi execessere 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 execnon è più pericoloso di un programma non sicuro che fa il danno da solo in quanto execnon dà alcun nuovo privilegio al programma.
Thomas Baruchel,

1
@ThomasBaruchel di nuovo, il mio punto è contrastare l'idea che eval o exec possano essere messi in sicurezza. In particolare, questa risposta afferma che il controllo dei locali e dei locali consentirà di usarli in sicurezza. Questo è falso. Ogni volta che usi exec ed eval, devi sapere con precisione quale codice viene eseguito. Se non lo fai, allora sei aperto a operazioni pericolose.
Ned Batchelder,

21

Ricorda che dalla versione 3 execè una funzione!
quindi usa sempre exec(mystring)invece di exec mystring.


11

eval()è solo per le espressioni, mentre eval('x+1')funziona, eval('x=1')per esempio non funzionerà. In tal caso, è meglio usare execo anche meglio: prova a trovare una soluzione migliore :)


11

Evita execeeval

L'uso di exece evalin Python è fortemente disapprovato.

Ci sono alternative migliori

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.

Non è idiomatico

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 execnon significa che dovresti usare quello strumento.

È pericoloso

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...

È difficile da leggere e capire

Da http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (sottolineatura mia):

Innanzitutto, execrende 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.


"La mia ipotesi migliore è che non puoi fare alcun male se non puoi usare doppi trattini bassi" - Potresti costruire una stringa contenente doppi trattini bassi e quindi chiamare eval quella stringa.
Stanley Bak,

un buon consiglio, a meno che tu non stia scrivendo un generatore di codice, o un corridore di lavoro o simili ... cosa che probabilmente la maggior parte delle persone qui sta facendo.
Erik Aronesty,

Devo importare un percorso relativo da un file config ( 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 reldiressere questa stringa letterale, non essere valutata, quindi non posso aprire un file reldir. Hai un suggerimento?
Marie. P.

9

Si esegue l'esecuzione del codice utilizzando exec, come con la seguente sessione IDLE:

>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']

4

1
Questo non funziona con Python normale. Almeno non in Python 3.
Thomas Ahle,

6

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)


4

Vale la pena ricordare che quel execfratello esiste così chiamato execfilese 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())



1

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


0

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.


0

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


1
Come hai gestito la sicurezza? Come fai a sapere che i clienti non abuseranno di questo privilegio?
JSBach
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.