W książce, którą czytam na temat Pythona, nadal używa kodu eval(input('blah'))
Przeczytałem dokumentację i rozumiem ją, ale wciąż nie widzę, jak zmienia ona input()
funkcję.
Co to robi? Czy ktoś może wyjaśnić?
W książce, którą czytam na temat Pythona, nadal używa kodu eval(input('blah'))
Przeczytałem dokumentację i rozumiem ją, ale wciąż nie widzę, jak zmienia ona input()
funkcję.
Co to robi? Czy ktoś może wyjaśnić?
Odpowiedzi:
Funkcja eval pozwala programowi Python uruchamiać kod Pythona wewnątrz siebie.
przykład eval (powłoka interaktywna):
>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
eval()
może być również używany do wykonywania wysoce dynamicznego kodu, ale przed użyciem go należy w pełni zdawać sobie sprawę z zagrożeń bezpieczeństwa i wydajności.
eval
ani nie może robić tego, co robi eval
.
eval
, poza niebezpieczeństwem, nie może uruchamiać całych programów, jak robi to w programie codepad, ponieważ może ocenić tylko jedno wyrażenie.
eval()
interpretuje ciąg jako kod. Powodem, dla którego tak wiele osób ostrzegło cię przed użyciem tego, jest to, że użytkownik może użyć tego jako opcji uruchamiania kodu na komputerze. Jeśli masz eval(input())
i os
zaimportowałeś, osoba może wpisać, w input()
os.system('rm -R *')
którym skasują wszystkie twoje pliki z twojego katalogu domowego. (Zakładając, że masz system uniksowy). Korzystanie eval()
jest dziurą w zabezpieczeniach. Jeśli chcesz przekonwertować ciągi znaków na inne formaty, spróbuj użyć rzeczy, które to robią, takich jak int()
.
eval
ze input()
jest dziura bezpieczeństwa. Nie umieszczaj input()
w ewaluacji oświadczenia, a wszystko będzie dobrze.
eval
jest w wielu przypadkach kwestia bezpieczeństwa.
input
zwykle pobiera dane z konsoli, użytkownik może po prostu wyjść z programu i wpisać rm -R *
mimo wszystko ...
Wiele dobrych odpowiedzi tutaj, ale żadna nie opisuje użycia eval()
w kontekście jego globals
i locals
kwargs, tj. eval(expression, globals=None, locals=None)
(Patrz eval
tutaj dokumenty ).
Można ich użyć do ograniczenia funkcji dostępnych za pośrednictwem eval
funkcji. Na przykład, jeśli załadujesz świeżego interpretera Pythona, locals()
i globals()
będą takie same i będą wyglądać mniej więcej tak:
>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
'__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
'__package__': None, '__name__': '__main__'}
Z pewnością w builtins
module znajdują się funkcje , które mogą wyrządzić znaczne szkody w systemie. Możliwe jest jednak zablokowanie czegokolwiek i wszystkiego, czego nie chcemy, aby były dostępne. Weźmy przykład. Powiedzmy, że chcemy zbudować listę reprezentującą domenę dostępnych rdzeni w systemie. Dla mnie mam 8 rdzeni, więc chciałbym listę [1, 8]
.
>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]
Podobnie wszystko __builtins__
jest dostępne.
>>>eval('abs(-1)')
1
Ok. Widzimy więc jedną funkcję, którą chcemy ujawnić, oraz przykład jednej (z wielu, które mogą być znacznie bardziej złożone) metody, której nie chcemy ujawniać. Więc zablokujmy wszystko.
>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable
Skutecznie zablokowaliśmy wszystkie __builtins__
funkcje i jako taki wprowadziliśmy poziom ochrony do naszego systemu. W tym momencie możemy zacząć dodawać funkcje, które chcemy udostępnić.
>>>from os import cpu_count
>>>exposed_methods = {'cpu_count': cpu_count}
>>>eval('cpu_count()', {'__builtins__':None}, exposed_methods)
8
>>>eval('abs(cpu_count())', {'__builtins__':None}, exposed_methods)
TypeError: 'NoneType' object is not subscriptable
Teraz mamy cpu_count
dostępną funkcję, wciąż blokując wszystko, czego nie chcemy. Moim zdaniem jest to bardzo potężne i wyraźnie z zakresu innych odpowiedzi, a nie powszechne wdrożenie. Istnieje wiele zastosowań czegoś takiego i pod warunkiem, że jest obsługiwany poprawnie, osobiście uważam, że eval
można go bezpiecznie wykorzystać w świetnej cenie.
NB
Kolejną fajną kwargs
rzeczą jest to, że możesz zacząć używać skrótu do swojego kodu. Załóżmy, że używasz eval jako części potoku do wykonania importowanego tekstu. Tekst nie musi mieć dokładnego kodu, może być zgodny z formatem plików szablonów i nadal wykonywać dowolne czynności. Na przykład:
>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
W Pythonie 2.x input(...)
jest równoważne zmianie nazwy eval(raw_input(...))
w Pythonie 3.x , co, jak podejrzewam, prowadzi do zamieszania (prawdopodobnie przeglądałeś dokumentację w Pythonie 2.x). Dodatkowo działałby dobrze w Pythonie 3.x, ale podniósłby a w Pythonie 2.raw_input
input
input
eval(input(...))
TypeError
W tym przypadku eval
służy do wymuszenia ciągu zwracanego z input
wyrażenia i interpretowanego. Ogólnie uważa się to za złą praktykę.
input
co oznacza to, co raw_input
zrobiono w wersji 2.x.
Może wprowadzający w błąd przykład czytania linii i interpretowania jej.
Spróbuj eval(input())
i wpisz "1+1"
- powinno to zostać wydrukowane 2
. Eval ocenia wyrażenia.
eval()
ocenia przekazany ciąg jako wyrażenie w języku Python i zwraca wynik. Na przykład eval("1 + 1")
interpretuje i wykonuje wyrażenie "1 + 1"
i zwraca wynik (2).
Jednym z powodów, dla których możesz się mylić, jest to, że cytowany kod zawiera pewien poziom pośredni. Wewnętrzne wywołanie funkcji (wejście) jest wykonywane jako pierwsze, więc użytkownik widzi monit „bla”. Wyobraźmy sobie, że odpowiadają „1 + 1” (cytaty dodane dla zachowania przejrzystości, nie wpisuj ich podczas uruchamiania programu), funkcja wejściowa zwraca ten ciąg, który jest następnie przekazywany do funkcji zewnętrznej (eval), która interpretuje ciąg i zwraca wynik (2).
Przeczytaj więcej o eval tutaj .
eval()
, jak sama nazwa wskazuje, ocenia przekazany argument.
raw_input()
jest teraz input()
w wersji Python 3.x. Najczęściej spotykanym przykładem użycia eval()
jest użycie tej funkcji w celu zapewnienia funkcjonalności, która jest input()
dostępna w wersji Pythona w wersji 2.x. raw_input zwrócił dane wprowadzone przez użytkownika jako ciąg, podczas gdy dane wejściowe oszacowały wartość wprowadzonych danych i zwróciły je.
eval(input("bla bla"))
w ten sposób replikuje funkcjonalność input()
w 2.x, tj. ocenę danych wprowadzonych przez użytkownika.
W skrócie: eval()
ocenia przekazane mu argumenty, a zatem eval('1 + 1')
zwrócił 2.
Jedną z przydatnych aplikacji eval()
jest ocena wyrażeń Pythona na podstawie ciągu znaków. Na przykład załaduj z reprezentacji ciągu słownika pliku:
running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()
Przeczytaj jako zmienną i edytuj:
fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction
Wynik:
{'Greeting': 'Hello world'}
eval
?
Spóźniłem się z odpowiedzią na to pytanie, ale wydaje się, że nikt nie udzielił jasnej odpowiedzi na to pytanie.
Jeśli użytkownik wprowadzi wartość liczbową, input()
zwróci ciąg.
>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'
Tak więc eval()
oceni zwracaną wartość (lub wyrażenie), która jest łańcuchem i zwraca liczbę całkowitą / liczbę zmiennoprzecinkową.
>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>>
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14
Oczywiście jest to zła praktyka. int()
lub float()
powinien być użyty zamiast eval()
w tym przypadku.
>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Inną opcją, jeśli chcesz ograniczyć łańcuch oceny do prostych literałów, jest użycie ast.literal_eval()
. Kilka przykładów:
import ast
# print(ast.literal_eval('')) # SyntaxError: unexpected EOF while parsing
# print(ast.literal_eval('a')) # ValueError: malformed node or string
# print(ast.literal_eval('import os')) # SyntaxError: invalid syntax
# print(ast.literal_eval('1+1')) # 2: but only works due to a quirk in parser
# print(ast.literal_eval('1*1')) # ValueError: malformed node or string
print(ast.literal_eval("{'a':1}")) # {'a':1}
Z dokumentów :
Bezpiecznie oceń węzeł wyrażenia lub ciąg znaków zawierający literał Pythona lub wyświetlacz kontenera. Podany ciąg znaków lub węzeł może składać się wyłącznie z następujących struktur literału Pythona: ciągów, bajtów, liczb, krotek, list, dykt, zestawów, boolanów i Brak.
Można to wykorzystać do bezpiecznej oceny ciągów zawierających wartości Pythona z niezaufanych źródeł bez konieczności samodzielnego analizowania wartości. To nie stanie ocenić dowolnie złożonych wyrażeń, na przykład z udziałem operatorów lub indeksowanie.
Jeśli chodzi o to, dlaczego jest tak ograniczony, z listy mailingowej :
Dopuszczanie wyrażeń operatora z literałami jest możliwe, ale o wiele bardziej skomplikowane niż bieżąca implementacja. Prosta implementacja nie jest bezpieczna: bez problemu można indukować praktycznie nieograniczone użycie procesora i pamięci (spróbuj „9 ** 9 ** 9” lub „[Brak] * 9 ** 9”).
Jeśli chodzi o użyteczność, funkcja ta jest przydatna do „odczytania” dosłownych wartości i pojemników, jak określono w repr (). Można to na przykład wykorzystać do serializacji w formacie, który jest podobny do JSON, ale bardziej wydajny.
ast.literal_eval
nie obsługuje operatorów, w przeciwieństwie do twojego '1+1'
przykładu. Niemniej jednak obsługuje listy, liczby, ciągi itp., A zatem jest dobrą alternatywą dla typowych eval
przypadków użycia.