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.