Jak wykonać ciąg zawierający kod Python w Pythonie?


357

Jak wykonać ciąg zawierający kod Python w Pythonie?


5
Szczerze mówiąc, w przeciwieństwie do drugiego pytania, to pytanie nie jest duplikatem. Inni zadali o wiele więcej podstawowych pytań niż to.
DNS

8
Prawidłowa odpowiedź to prawie zawsze „nie!”.
bobince

23
@S. Lott: Nie czytałeś ostatnio FAQ? „Można również zadawać własne pytania dotyczące programowania i odpowiadać na nie, ale udawać, że jesteś w Jeopardy: sformułuj je w formie pytania”. To nie jest złe pytanie. +1
Devin Jeanpierre

49
@ S.Lott: Nie. Nie musisz. Jeśli pytania nie ma jeszcze na stronie, oznacza to, że jest to uczciwa gra (zgodnie z FAQ, jak już wspomniano). Odpowiedz tylko na każde pytanie, tak jakby PO potrzebuje pomocy. Być może nie, ale prawdopodobnie zrobi to następny facet, który przeczyta ich pytanie. Tylko moje 2 centy.
Bill the Lizard

1
Do wszystkich, którzy mówią, żeby tego nie robić: mam przypadek, w którym absolutnie muszę. Obejmuje przetwarzanie rozproszone.
sudo

Odpowiedzi:


331

W przypadku instrukcji użyj exec(string)(Python 2/3) lub exec string(Python 2):

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

Jeśli potrzebujesz wartości wyrażenia, użyj eval(string):

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

Jednak pierwszym krokiem powinno być zadanie sobie pytania, czy naprawdę potrzebujesz. Wykonywanie kodu powinno zasadniczo być ostatecznością: jest powolne, brzydkie i niebezpieczne, jeśli może zawierać kod wprowadzony przez użytkownika. Zawsze powinieneś najpierw spojrzeć na alternatywy, takie jak funkcje wyższego rzędu, aby sprawdzić, czy mogą one lepiej zaspokoić twoje potrzeby.


1
ale co z zakresem kodu wykonywanego przez „exec”? czy jest zagnieżdżony?
jondinham,

21
Częstym przypadkiem, gdy ktoś chce użyć „exec”, jest coś w rodzaju if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42itp., Które może następnie zapisać jako exec ("x.%s = 42" % s). W tym typowym przypadku (gdy potrzebujesz tylko dostępu do atrybutu obiektu przechowywanego w ciągu), istnieje znacznie szybsza, czystsza i bezpieczniejsza funkcja getattr: po prostu pisz, getattr(x, s) = 42aby oznaczać to samo.
ShreevatsaR

5
W jaki sposób exec działa wolniej niż interpreter Pythona?
Cris Stringfellow

6
@ShreevatsaR nie masz na myśli setattr(x, s, 42)? Próbowałem getattr(x, 2) = 42i nie udało sięcan't assign to function call: <string>, line 1
Tanner Semerad,

6
@Tanner: Hmm. Tak, rzeczywiście setattr(x, s, 42)jest poprawna składnia. Zaskoczony, że tak długo zajęło wykrycie tego błędu. W każdym razie chodzi o to, że getattri setattrsą alternatywą dla execkiedy chcesz to dostać dowolny element, spojrzał w górę przez struny.
ShreevatsaR

68

W tym przykładzie łańcuch jest wykonywany jako kod za pomocą funkcji 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
zamiana stdout i stderr w ten sposób bardzo mnie denerwuje. wygląda na to, że może powodować ogromne problemy z bezpieczeństwem. czy jest na to jakiś sposób?
Narcolapser

1
@Narcolapser powinieneś być bardziej zainteresowany używaniem exec(chyba że wiesz, że ciąg kodu pochodzi z zaufanego źródła).
bruno desthuilliers

27

evali execsą właściwym rozwiązaniem, i można je stosować w bezpieczniejszy sposób.

Jak omówiono w podręczniku Pythona i jasno wyjaśniono w tym samouczku, funkcje evali execpobierają dwa dodatkowe parametry, które pozwalają użytkownikowi określić, jakie globalne i lokalne funkcje i zmienne są dostępne.

Na przykład:

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

Zasadniczo definiujesz przestrzeń nazw, w której kod będzie wykonywany.


9
Nie można uczynić eval bezpiecznym: Eval jest naprawdę niebezpieczny . Jeśli weźmiesz ode mnie kod i sprawdzisz go, mogę segfaultować twój program Python. Koniec gry.
Ned Batchelder,

3
@ v.oddou Odpowiedziałem na oświadczenie Alana: „eval and exec .. można używać w bezpieczny sposób”. To nieprawda. Gdyby ktoś powiedział „bash może być używany w bezpieczny sposób”, byłoby to również fałszywe. Bash jest niebezpieczny. Jest to konieczne niebezpieczeństwo, ale mimo wszystko niebezpieczne. Twierdzenie, że eval można zabezpieczyć, jest po prostu błędne.
Ned Batchelder,

1
@NedBatchelder rzeczywiście tak. a link, który wskazujesz, jest dobrym materiałem. wraz z mocą przychodzi odpowiedzialność, więc chodzi po prostu o świadomość potencjalnej mocy ewaluacji. a jeśli zdecydujemy, że moc = niebezpieczeństwo.
v.oddou

3
@NedBatchelder Wiele fragmentów kodu napisanych w Pythonie może być również niebezpiecznych, ale dlaczego zakładasz, że evallub execmają być używane jako exec(input("Type what you want"))? Istnieje wiele przypadków, w których program może napisać procedurę lub funkcję w wyniku obliczeń; wynikające z tego funkcje będą tak bezpieczne i tak szybkie (raz ocenione), jak każda inna część dobrego i dobrze napisanego programu. Niebezpieczny program zawierający execnie jest bardziej niebezpieczny niż niebezpieczny program sam wyrządzający szkodę, ponieważ execnie daje żadnego nowego przywileju programowi.
Thomas Baruchel

1
@ThomasBaruchel ponownie, moim celem jest przeciwstawienie się temu, że eval lub exec można zabezpieczyć. W szczególności ta odpowiedź twierdzi, że kontrolowanie globali i mieszkańców umożliwi bezpieczne korzystanie z nich. To nieprawda. Za każdym razem, gdy używasz exec i eval, musisz dokładnie wiedzieć, jaki kod jest wykonywany. Jeśli tego nie zrobisz, jesteś otwarty na niebezpieczne operacje.
Ned Batchelder

21

Pamiętaj, że od wersji 3 execjest funkcją!
więc zawsze używaj exec(mystring)zamiast exec mystring.


11

eval()służy tylko do wyrażeń, podczas gdy eval('x+1')działa, eval('x=1')na przykład nie działa. W takim przypadku lepiej jest użyć exec, a nawet lepiej: spróbuj znaleźć lepsze rozwiązanie :)


11

Unikaj execieval

Używanie execiw evalPythonie jest bardzo niezadowolone.

Istnieją lepsze alternatywy

Od najwyższej odpowiedzi (moje podkreślenie):

W przypadku wyciągów użyj exec.

Jeśli potrzebujesz wartości wyrażenia, użyj eval.

Jednak pierwszym krokiem powinno być zadanie sobie pytania, czy naprawdę potrzebujesz. Wykonywanie kodu powinno zasadniczo być ostatecznością : jest powolne, brzydkie i niebezpieczne, jeśli może zawierać kod wprowadzony przez użytkownika. Zawsze powinieneś najpierw spojrzeć na alternatywy, takie jak funkcje wyższego rzędu , aby sprawdzić, czy mogą one lepiej zaspokoić twoje potrzeby.

Od alternatyw do exec / eval?

ustaw i pobierz wartości zmiennych o nazwach w ciągach

[chociaż eval] działałoby, generalnie nie zaleca się używania nazw zmiennych mających znaczenie dla samego programu.

Zamiast tego lepiej użyj słownika.

To nie jest idiomatyczne

From http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (moje podkreślenie)

Python nie jest PHP

Nie próbuj obchodzić idiomów Pythona, ponieważ jakiś inny język robi to inaczej. Przestrzenie nazw znajdują się w Pythonie z jakiegoś powodu i tylko dlatego, że daje ci narzędzie exec, nie oznacza to, że powinieneś używać tego narzędzia.

To jest niebezpieczne

From http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (moje podkreślenie)

Tak więc eval nie jest bezpieczny, nawet jeśli usuniesz wszystkie globals i wbudowane!

Problemem wszystkich tych prób ochrony eval () jest to, że są czarnymi listami . Wyraźnie usuwają rzeczy, które mogą być niebezpieczne. To przegrana bitwa, ponieważ jeśli z listy pozostanie tylko jeden przedmiot, możesz zaatakować system .

Czy więc eval można zabezpieczyć? Ciężko powiedzieć. W tym momencie sądzę, że nie możesz wyrządzić krzywdy, jeśli nie możesz użyć podwójnych znaków podkreślenia, więc może wykluczając dowolny ciąg znaków z podwójnymi znakami podkreślenia, jesteś bezpieczny. Może...

Trudno to przeczytać i zrozumieć

From http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (moje podkreślenie):

Po pierwsze, execludziom trudniej jest odczytać kod . Aby dowiedzieć się, co się dzieje, nie muszę po prostu czytać twojego kodu, muszę czytać twój kod, dowiedzieć się, jaki ciąg wygeneruje, a następnie przeczytaj ten wirtualny kod. Tak więc, jeśli pracujesz w zespole, publikujesz oprogramowanie typu open source lub prosisz o pomoc w stylu StackOverflow, utrudniasz innym ludziom pomoc. A jeśli istnieje szansa, że ​​za 6 miesięcy będziesz debugować lub rozwijać ten kod, bezpośrednio utrudniasz sobie pracę.


„zgaduję, że nie możesz wyrządzić żadnej krzywdy, jeśli nie możesz użyć podwójnych znaków podkreślenia” - możesz zbudować ciąg zawierający podwójne znaki podkreślenia, a następnie wywołać eval ten ciąg.
Stanley Bak,

dobra rada, chyba że piszesz generator kodu, narzędzie do wykonywania pracy lub podobne ... co większość ludzi tutaj robi.
Erik Aronesty

Muszę zaimportować ścieżkę względną z pliku config ( cfg.yaml): reldir : ../my/dir/ i reldir = cfg[reldir]. Ponieważ jednak ten kod Pythona powinien działać zarówno w systemie Windows, jak i Linux, potrzebuję go, aby dostosować się do różnych dzielników ścieżek systemów operacyjnych; albo \\ albo /. Więc używam reldir : os.path.join('..','my','dir')w pliku konfiguracyjnym. Ale to tylko skutkuje reldirtym, że jest to ciąg literalny, a nie jest oceniany, więc nie mogę otworzyć pliku reldir. Czy masz jakieś sugestie?
Marie. P.

9

Wykonujesz kod za pomocą exec, podobnie jak w przypadku następnej sesji IDLE:

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

4

1
To nie działa w normalnym pythonie. Przynajmniej nie w pythonie 3.
Thomas Ahle,

6

Jak wspomniano inni, jest to „exec” ..

ale w przypadku, gdy kod zawiera zmienne, możesz użyć „globalnego”, aby uzyskać do niego dostęp, aby zapobiec kompilatorowi wywołania następującego błędu:

NameError: nazwa „p_variable” nie jest zdefiniowana

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


4

Warto wspomnieć, że ten execbrat istnieje również nazywany, execfilejeśli chcesz wywołać plik python. Czasami jest to dobre, jeśli pracujesz w pakiecie strony trzeciej, który zawiera straszne IDE i chcesz kodować poza jego pakietem.

Przykład:

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

lub:

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



1

Próbowałem całkiem sporo rzeczy, ale jedyne, co zadziałało, to:

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

wynik:

10


0

Najbardziej logicznym rozwiązaniem byłoby użycie wbudowanej funkcji eval (). Innym rozwiązaniem jest zapisanie tego łańcucha do tymczasowego pliku python i wykonanie go.


0

Ok .. Wiem, że to nie jest dokładnie odpowiedź, ale być może uwaga dla ludzi, którzy patrzą na to tak jak ja. Chciałem wykonać określony kod dla różnych użytkowników / klientów, ale chciałem również uniknąć wykonania / ewaluacji. Początkowo chciałem przechowywać kod w bazie danych dla każdego użytkownika i robić powyższe.

Skończyło się to na tworzeniu plików w systemie plików w folderze „customer_filters” i korzystaniu z modułu „imp”, jeśli dla tego klienta nie zastosowano żadnego filtra, to po prostu kontynuowano

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)

więc nazwa_klienta = „jj” wykona filtr_pliku_zastosowania z pliku filtry_klienta \ jj_filter.py


1
Jak zarządzałeś bezpieczeństwem? Skąd wiesz, że klienci nie będą nadużywać tego przywileju?
JSBach
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.