Jak wykonać ciąg zawierający kod Python w Pythonie?
Jak wykonać ciąg zawierający kod Python w Pythonie?
Odpowiedzi:
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.
if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42
itp., 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) = 42
aby oznaczać to samo.
setattr(x, s, 42)
? Próbowałem getattr(x, 2) = 42
i nie udało sięcan't assign to function call: <string>, line 1
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 getattr
i setattr
są alternatywą dla exec
kiedy chcesz to dostać dowolny element, spojrzał w górę przez struny.
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()
exec
(chyba że wiesz, że ciąg kodu pochodzi z zaufanego źródła).
eval
i exec
są 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 eval
i exec
pobierają 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.
eval
lub exec
mają 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 exec
nie jest bardziej niebezpieczny niż niebezpieczny program sam wyrządzający szkodę, ponieważ exec
nie daje żadnego nowego przywileju programowi.
exec
ieval
exec
iw eval
Pythonie jest bardzo niezadowolone.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.
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.
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.
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...
From http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (moje podkreślenie):
Po pierwsze,
exec
ludziom 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ę.
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 reldir
tym, że jest to ciąg literalny, a nie jest oceniany, więc nie mogę otworzyć pliku reldir
. Czy masz jakieś sugestie?
Wykonujesz kod za pomocą exec, podobnie jak w przypadku następnej sesji IDLE:
>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']
4
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)
Warto wspomnieć, że ten exec
brat istnieje również nazywany, execfile
jeś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())
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
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