Wie führe ich eine Zeichenfolge aus, die Python-Code in Python enthält?

357

Wie führe ich eine Zeichenfolge aus, die Python-Code in Python enthält?

Hekevintran
quelle
5
Um fair zu sein, ist diese Frage im Gegensatz zur anderen Frage kein Duplikat. Und andere haben viel grundlegendere Fragen gestellt.
DNS
8
Die richtige Antwort lautet natürlich fast immer „nicht!“.
Bobince
23
@S. Lott: Hast du die FAQ in letzter Zeit nicht gelesen? "Es ist auch vollkommen in Ordnung, Ihre eigene Programmierfrage zu stellen und zu beantworten, aber tun Sie so, als wären Sie in Gefahr: Formulieren Sie sie in Form einer Frage." Das ist keine schlechte Frage. +1
Devin Jeanpierre
49
@ S.Lott: Das tust du nicht. Das musst du nicht. Wenn die Frage noch nicht auf der Website ist, handelt es sich um ein faires Spiel (laut FAQ, wie bereits erwähnt). Beantworten Sie einfach jede Frage, als ob das OP Hilfe benötigt. Sie mögen es nicht, aber der nächste, der ihre Frage liest, wird es wahrscheinlich tun. Nur meine 2 Cent.
Bill the Lizard
1
An alle, die sagen, dass Sie es nicht tun sollen: Ich habe einen Fall, in dem ich es unbedingt muss. Es handelt sich um eine verteilte Verarbeitung.
Sudo

Antworten:

331

Verwenden Sie für Anweisungen exec(string)(Python 2/3) oder exec string(Python 2):

>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world

Wenn Sie den Wert eines Ausdrucks benötigen, verwenden Sie eval(string):

>>> x = eval("2+2")
>>> x
4

Der erste Schritt sollte jedoch sein, sich zu fragen, ob Sie es wirklich brauchen. Das Ausführen von Code sollte im Allgemeinen die letzte Instanz sein: Es ist langsam, hässlich und gefährlich, wenn es vom Benutzer eingegebenen Code enthalten kann. Sie sollten immer zuerst nach Alternativen suchen, z. B. nach Funktionen höherer Ordnung, um festzustellen, ob diese Ihren Anforderungen besser entsprechen können.

Brian
quelle
1
aber wie steht es mit dem Umfang des von 'exec' ausgeführten Codes? ist es verschachtelt?
Jondinham
21
Ein häufiger Fall, in dem jemand 'exec' verwenden möchte, ist so etwas wie if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42etc, als das er dann schreiben kann exec ("x.%s = 42" % s). In diesem häufigen Fall (in dem Sie nur auf das Attribut eines Objekts zugreifen müssen, das in einer Zeichenfolge gespeichert ist) gibt es eine viel schnellere, sauberere und sicherere Funktion getattr: Schreiben Sie einfach, getattr(x, s) = 42um dasselbe zu bedeuten.
ShreevatsaR
5
Wie ist exec langsamer als der Python-Interpreter?
Cris Stringfellow
6
@ShreevatsaR meinst du nicht setattr(x, s, 42)? Ich habe es versucht getattr(x, 2) = 42und es ist fehlgeschlagen mitcan't assign to function call: <string>, line 1
Tanner Semerad
6
@ Gerber: Hmm. Ja, in der Tat setattr(x, s, 42)ist die richtige Syntax. Überrascht dauerte es so lange, bis dieser Fehler erkannt wurde. Wie auch immer, der Punkt ist , dass getattrund setattrist eine Alternative zu , execwenn alles , was Sie wollen , ist ein beliebiges Mitglied zu bekommen, sah durch Zeichenfolge auf.
ShreevatsaR
68

Im Beispiel wird eine Zeichenfolge mit der Funktion exec als Code ausgeführt.

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()
Hekevintran
quelle
1
stdout und stderr so auszutauschen macht mich sehr nervös. Dies scheint große Sicherheitsprobleme zu verursachen. Gibt es einen Weg, das zu umgehen?
Narcolapser
1
@Narcolapser Sie sollten sich mehr mit der Verwendung befassen exec(es sei denn, Sie wissen, dass die Codezeichenfolge aus einer vertrauenswürdigen Quelle stammt).
Bruno Desthuilliers
27

evalund execsind die richtige Lösung, und sie können sicherer verwendet werden werden.

Wie im Referenzhandbuch von Python erläutert und in diesem Lernprogramm klar erläutert , werden die evalundexec nehmen Funktionen zwei zusätzliche Parameter , die ein Benutzer angeben lassen , was globale und lokale Funktionen und Variablen zur Verfügung stehen.

Zum Beispiel:

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

Im Wesentlichen definieren Sie den Namespace, in dem der Code ausgeführt wird.

Alan
quelle
9
Es ist nicht möglich, eval sicher zu machen: Eval ist wirklich gefährlich . Wenn Sie Code von mir nehmen und bewerten, kann ich Ihr Python-Programm fehlerhaft ausführen. Spiel ist aus.
Ned Batchelder
3
@ v.oddou Ich antwortete auf Alan's Aussage: "eval und exec .. können auf sichere Weise verwendet werden." Das ist falsch. Wenn jemand sagte, "Bash kann auf sichere Weise verwendet werden", wäre das auch falsch. Bash ist gefährlich. Es ist eine notwendige Gefahr, aber dennoch gefährlich. Die Behauptung, dass Eval sicher gemacht werden kann, ist einfach falsch.
Ned Batchelder
1
@NedBatchelder ja in der Tat. und der Link, auf den Sie verweisen, ist gutes Material. Mit der Macht geht die Verantwortung einher, daher geht es einfach darum, sich der potenziellen Macht der Bewertung bewusst zu sein. und wenn wir entscheiden, dass Macht = Gefahr.
v.oddou
3
@NedBatchelder Viele in Python geschriebene Codeteile können ebenfalls gefährlich sein, aber warum nehmen Sie das an evaloder execsollen als verwendet werden exec(input("Type what you want"))? Es gibt viele Fälle, in denen ein Programm als Ergebnis einer Berechnung eine Prozedur oder eine Funktion schreiben kann. Die resultierenden Funktionen sind so sicher und schnell (einmal evaluiert) wie jeder andere Teil eines guten und gut geschriebenen Programms. Ein unsicheres Programm, das enthält, execist nicht gefährlicher als ein unsicheres Programm, das den Schaden selbst verursacht, da execes dem Programm keine neuen Berechtigungen verleiht.
Thomas Baruchel
1
@ThomasBaruchel noch einmal, mein Punkt ist es, der Vorstellung entgegenzuwirken, dass eval oder exec sicher gemacht werden können. Diese Antwort behauptet insbesondere, dass die Kontrolle der Globalen und Einheimischen es ermöglichen wird, sie sicher zu verwenden. Das ist falsch. Jedes Mal, wenn Sie exec und eval verwenden, müssen Sie genau wissen, welcher Code ausgeführt wird. Wenn Sie dies nicht tun, sind Sie offen für gefährliche Operationen.
Ned Batchelder
21

Denken Sie daran, dass ab Version 3 execeine Funktion ist!
also immer exec(mystring)anstelle von verwenden exec mystring.

bheks
quelle
11

eval()ist nur für Ausdrücke, während es eval('x+1')funktioniert, eval('x=1')funktioniert es zum Beispiel nicht. In diesem Fall ist es besser zu verwenden execoder noch besser: versuchen Sie eine bessere Lösung zu finden :)

LGB
quelle
11

Vermeiden Sie execundeval

Die Verwendung von execund evalin Python ist sehr verpönt.

Es gibt bessere Alternativen

Von der obersten Antwort (Hervorhebung meiner):

Verwenden Sie für Anweisungen exec.

Wenn Sie den Wert eines Ausdrucks benötigen, verwenden Sie eval.

Der erste Schritt sollte jedoch sein, sich zu fragen, ob Sie es wirklich brauchen. Das Ausführen von Code sollte im Allgemeinen die letzte Instanz sein : Es ist langsam, hässlich und gefährlich, wenn es vom Benutzer eingegebenen Code enthalten kann. Sie sollten immer zuerst nach Alternativen suchen , z. B. nach Funktionen höherer Ordnung , um festzustellen, ob diese Ihren Anforderungen besser entsprechen können.

Von Alternativen zu Exec / Eval?

Setzen und Abrufen von Variablenwerten mit den Namen in Zeichenfolgen

[während eval ] würde funktionieren, es wird im Allgemeinen nicht empfohlen, Variablennamen zu verwenden, die für das Programm selbst eine Bedeutung haben.

Verwenden Sie stattdessen besser ein Diktat.

Es ist nicht idiomatisch

Von http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (Schwerpunkt Mine)

Python ist kein PHP

Versuchen Sie nicht, Python-Redewendungen zu umgehen, da dies in einer anderen Sprache anders ist. Namespaces befinden sich aus einem bestimmten Grund in Python. Nur weil Sie das Tool erhalten, execheißt das nicht, dass Sie dieses Tool verwenden sollten.

Es ist gefährlich

Von http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (Hervorhebung von mir)

Eval ist also nicht sicher, selbst wenn Sie alle Globals und Builtins entfernen!

Das Problem bei all diesen Versuchen, eval () zu schützen, ist, dass es sich um schwarze Listen handelt . Sie entfernen explizit Dinge, die gefährlich sein könnten. Das ist ein verlorener Kampf, denn wenn nur noch ein Gegenstand von der Liste übrig ist, können Sie das System angreifen .

Kann eval also sicher gemacht werden? Schwer zu sagen. An dieser Stelle gehe ich davon aus, dass Sie keinen Schaden anrichten können, wenn Sie keine doppelten Unterstriche verwenden können. Wenn Sie also einen String mit doppelten Unterstrichen ausschließen, sind Sie sicher. Könnte sein...

Es ist schwer zu lesen und zu verstehen

Von http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (Hervorhebung von mir):

Erstens execist es für Menschen schwieriger, Ihren Code zu lesen . Um herauszufinden, was passiert, muss ich nicht nur Ihren Code lesen, sondern auch Ihren Code lesen, herausfinden, welche Zeichenfolge generiert wird, und dann diesen virtuellen Code lesen. Wenn Sie also in einem Team arbeiten, Open Source-Software veröffentlichen oder irgendwo wie StackOverflow um Hilfe bitten, erschweren Sie es anderen, Ihnen zu helfen. Und wenn es eine Chance gibt, dass Sie diesen Code in 6 Monaten debuggen oder erweitern, machen Sie es sich direkt schwerer.

Caridorc
quelle
"Meine beste Vermutung ist, dass Sie keinen Schaden anrichten können, wenn Sie keine doppelten Unterstriche verwenden können." - Sie könnten eine Zeichenfolge mit doppelten Unterstrichen erstellen und diese Zeichenfolge dann eval aufrufen.
Stanley Bak
Guter Rat, es sei denn, Sie schreiben einen Codegenerator, einen Job Runner oder ähnliches ... was die meisten Leute hier wahrscheinlich tun.
Erik Aronesty
Ich muss einen relativen Pfad aus einer Konfigurationsdatei ( cfg.yaml) importieren:, reldir : ../my/dir/ und reldir = cfg[reldir]. Da dieser Python-Code jedoch sowohl unter Windows als auch unter Linux ausgeführt werden soll, muss er an die verschiedenen Pfadteiler der Betriebssysteme angepasst werden. entweder \\ oder /. Also benutze ich reldir : os.path.join('..','my','dir')in der Konfigurationsdatei. Dies führt jedoch nur dazu, reldirdass diese Literalzeichenfolge nicht ausgewertet wird, sodass ich keine Datei in öffnen kann reldir. Hast du einen vorschlag
Marie. P.
9

Sie führen die Ausführung von Code mit exec aus, wie in der folgenden IDLE-Sitzung:

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

4
gus
quelle
1
Dies funktioniert in normalem Python nicht. Zumindest nicht in Python 3.
Thomas Ahle
6

Wie die anderen erwähnten, ist es "exec" ..

Falls Ihr Code jedoch Variablen enthält, können Sie mit "global" darauf zugreifen, um zu verhindern, dass der Compiler den folgenden Fehler auslöst:

NameError: Der Name 'p_variable' ist nicht definiert

exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
Ghanem
quelle
5

Verwenden Sie eval .

Pablo Santa Cruz
quelle
7
Eval () führt keine Anweisungen aus.
RickyA
4

Es ist erwähnenswert, dass der execBruder auch so genannt wirdexecfile wenn Sie eine Python-Datei aufrufen möchten. Das ist manchmal gut, wenn Sie in einem Paket eines Drittanbieters arbeiten, in dem schreckliche IDEs enthalten sind, und wenn Sie außerhalb des Pakets codieren möchten.

Beispiel:

execfile('/path/to/source.py)'

oder:

exec(open("/path/to/source.py").read())

user1767754
quelle
3

Check out eval :

x = 1
print eval('x+1')
->2
Ryeguy
quelle
10
Eval () führt keine Anweisungen aus.
RickyA
1

Ich habe einige Dinge ausprobiert, aber das einzige, was funktioniert hat, war das Folgende:

temp_dict = {}
exec("temp_dict['val'] = 10") 
print(temp_dict['val'])

Ausgabe:

10

Paul-Shuvo
quelle
0

Die logischste Lösung wäre die Verwendung der integrierten Funktion eval (). Eine andere Lösung besteht darin, diese Zeichenfolge in eine temporäre Python-Datei zu schreiben und auszuführen.

John T.
quelle
0

Ok .. Ich weiß, das ist nicht gerade eine Antwort, aber möglicherweise eine Notiz für Leute, die das so sehen wie ich. Ich wollte spezifischen Code für verschiedene Benutzer / Kunden ausführen, aber auch die Ausführung / Auswertung vermeiden. Ich habe zunächst versucht, den Code für jeden Benutzer in einer Datenbank zu speichern und die oben genannten Schritte auszuführen.

Am Ende habe ich die Dateien im Dateisystem in einem Ordner 'customer_filters' erstellt und das Modul 'imp' verwendet. Wenn für diesen Kunden kein Filter angewendet wurde, wurde es einfach fortgesetzt

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)

Daher würde customerName = "jj" apply_address_filter aus der Datei customer_filters \ jj_filter.py ausführen

Brian
quelle
1
Wie haben Sie die Sicherheit verwaltet? Woher wissen Sie, dass Kunden dieses Privileg nicht missbrauchen?
JSBach