Python'un eval () işlevi ne yapar?


306

Python'da okuduğum kitapta, kodu kullanmaya devam ediyor eval(input('blah'))

Belgeleri okudum ve anlıyorum, ancak hala input()işlevi nasıl değiştirdiğini göremiyorum .

Bu ne işe yarıyor? Birisi açıklayabilir mi?


4
Eval fonksiyonu, kendisine iletilen dizeyi (argüman) python kodu olarak çalıştırmaya ve yorumlamaya çalışır. x = 1 print (eval ('x + 1')) Yukarıdaki kodun çıktısı 2 olacaktır. Bu tür bir yaklaşımın dezavantajı, kullanıcının tahribat koşullarına yol açabilecek kod yazma bağımsızlığından yararlanmasıdır. eval fonksiyonunda global ve lokal parametreleri geçirerek birçok değişken ve yönteme ulaşmak.
ATIF IBAD KHAN

Yanıtlar:


276

Eval fonksiyonu bir Python programının kendi içinde Python kodunu çalıştırmasını sağlar.

eval örneği (etkileşimli kabuk):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1

25
haha, bu önemsiz bir örnekti, ancak kullanıcının rasgele bir komut yazmasına ve python'un çalıştırmasına izin verebilirsiniz. Böylece, kullanıcı bir komut dizesinde yazabilir ve daha sonra python'un kod olarak çalıştırmasını sağlayabilirsiniz. Örneğin: eval ("__ import __ ('os'). Remove ('file')").
BYS2

60
İhtiyacınız olana kadar işe yaramaz görünecektir. Bir test ortamında komut dosyalarını yürütmenize izin vermek için codepad.org gibi sitelerde kullanılır. eval()yüksek dinamik kod yürütmek için de kullanılabilir, ancak kullanmadan önce güvenlik ve performans risklerinden tam olarak haberdar olmalısınız.
George Cummins,

6
@GeorgeCummins, codepad.org kullanmaz evalve ne yaptığını da yapamaz eval.
Mike Graham

16
@GeorgeCummins: codepag.org her şeyi bir sanal alanda çalıştırır: kötü amaçlı kodun kötü bir şey yapmasını önlemek için sanal bir makinede ptrace kontrolleri olan bir cezaevi hapishanesi. Basit bir değerlendirmeden çok daha karmaşıktır. Ayrıca eval Python'a özgüdür. codepad birçok dili destekler.
FogleBird

4
@GeorgeCummins, codepad, keyfi programları güvenle çalıştırmak için çok karmaşık bir sistem çalıştırır. eval, güvensiz olmaktan başka, yalnızca tek bir ifadeyi değerlendirebildiğinden kod alanı gibi tüm programları çalıştıramaz.
Mike Graham

165

eval()bir dizeyi kod olarak yorumlar. Bu kadar çok insanın bunu kullanma konusunda sizi uyarmasının nedeni, kullanıcının bunu bilgisayarda kod çalıştırmak için bir seçenek olarak kullanabilmesidir. Girdiyseniz eval(input())ve osiçe aktardıysanız, bir kişi input() os.system('rm -R *')ana dizininizdeki tüm dosyalarınızı silecek bir kullanıcı yazabilir . (Unix sisteminiz olduğu varsayılarak). Kullanmak eval()bir güvenlik deliğidir. Dizeleri başka biçimlere dönüştürmeniz gerekiyorsa, bunu yapan şeyleri kullanmayı deneyin int().


14
Sen kullanarak demek evalolan input()bir güvenlik delik. input()Bir değerlendirme ifadesi içine koymayın ve iyi olacaksınız.
Rohmer

19
@Rohmer, güvenli olmayan veriler her yerden gelebilir: web istekleri, form giriş alanları, dosya okumaları, ... sadece konsol girişinden değil. Dosyaları kendiniz yazsanız bile, başlangıçta güvenilir olmayan bir kaynaktan gelen girdi içerebilir. Yani evalbirçok durumda bir güvenlik sorunudur.
sanderd17

3
çünkü inputgenellikle konsoldan verilerini alır kullanıcı sadece programdan çıkmak ve yazabilirsiniz rm -R *... neyse
cz

63

Burada birçok iyi cevap var, ama hiçbiri eval()onun globalsve localskwarglar bağlamında kullanımını açıklamıyor, yani eval(expression, globals=None, locals=None)( eval buradaki dokümanlara bakınız ).

Bunlar, işlev aracılığıyla kullanılabilen işlevleri sınırlamak için kullanılabilir eval. Örneğin, yeni bir python yorumlayıcısı yüklerseniz locals()ve globals()aynı olacak ve şöyle görünecektir:

>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
 '__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
 '__package__': None, '__name__': '__main__'}

builtinsModül içerisinde bir sisteme ciddi hasar verebilecek işlevler vardır . Ancak mevcut olmasını istemediğimiz her şeyi ve her şeyi engellemek mümkündür. Bir örnek verelim. Diyelim ki bir sistemdeki mevcut çekirdeklerin bir alanını temsil edecek bir liste oluşturmak istiyoruz. Benim için 8 çekirdek var, bu yüzden bir liste istiyorum [1, 8].

>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]

Aynı şekilde hepsi __builtins__mevcuttur.

>>>eval('abs(-1)')
1

Tamam. Bu yüzden orada açığa çıkmak istediğimiz bir fonksiyon ve açığa çıkmak istemediğimiz bir (çok daha karmaşık olabilen) yöntem örneği görüyoruz. Öyleyse her şeyi engelleyelim.

>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable

Tüm __builtins__fonksiyonları etkin bir şekilde engelledik ve bu nedenle sistemimize bir düzeyde koruma getirdik. Bu noktada açığa çıkarmak istediğimiz işlevleri tekrar eklemeye başlayabiliriz.

>>>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

Şimdi cpu_counthala istemediğimiz her şeyi engellerken kullanılabilir fonksiyona sahibiz. Benim düşünceme göre, bu süper güçlü ve açıkça diğer uygulamaların kapsamından değil, ortak bir uygulama değil. Bunun gibi bir şey için çok sayıda kullanım vardır ve doğru bir şekilde ele alındığı sürece kişisel evalolarak çok değerli bir şekilde güvenle kullanılabileceğini hissediyorum .

NB

Bunlar hakkında harika bir şey kwargs, kodunuz için steno kullanmaya başlayabilirsiniz. Diyelim ki içe aktarılmış bazı metinleri yürütmek için bir ardışık düzenin bir parçası olarak eval kullandığınızı varsayalım. Metnin tam kodu olması gerekmez, bazı şablon dosya biçimini izleyebilir ve yine de istediğiniz herhangi bir şeyi yürütebilir. Örneğin:

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]

29

Python 2.x input(...)eşdeğerdir eval(raw_input(...)), Python 3.x raw_inputyeniden adlandırıldı input, hangi karışıklığa yol açtığından şüpheleniyorum (muhtemelen inputPython 2.x için belgelere bakıyordunuz ). Ayrıca, eval(input(...))Python 3.x iyi çalışır, ancak TypeErrorPython 2 yükseltir .

Bu durumda eval, döndürülen dizgiyi inputbir ifadeye zorlamak ve yorumlamak için kullanılır. Genellikle bu kötü uygulama olarak kabul edilir.


Ya da bir Python 3.x kitabı, 2.x'te inputne olduğu anlamına geliyor raw_input.
dan04

1
Evet, ilk cevabımı yazdıktan sonra başıma geldi ve bu durum böyledir.
zeekay

6

Belki bir çizgi okuma ve yorumlama yanıltıcı bir örnek.

Deneyin eval(input())ve yazın "1+1"- bu yazdırılmalıdır 2. Eval ifadeleri değerlendirir.


Neden tırnak arasına yazmalıyım? Giriş bir dize alıyorum ve kod yürütmek değil, eval geçmek için, bu yüzden ben sadece 1 + 1 yazmış olsaydım iyi olurdu ... ¿?
JC Rocamonde

Mesele şu ki P2.x ve 3.x'i karıştırıyorsunuz. Python 2'de kodunuz çalışır, ancak iki kez değerlendirme yapmak mantıklı değildir. Python 3'te bunu yapmaz ve bir dize döndürür.
JC Rocamonde

6

eval()iletilen dizeyi Python ifadesi olarak değerlendirir ve sonucu döndürür. Örneğin eval("1 + 1"), ifadeyi yorumlar ve yürütür "1 + 1"ve sonucu döndürür (2).

Kafanızın karışmış olmasının bir nedeni, belirttiğiniz kodun bir miktar dolaylı işlem içermesidir. Önce iç işlev çağrısı (giriş) yürütülür, böylece kullanıcı "blah" istemini görür. "1 + 1" ile yanıt verdiklerini düşünelim (netlik için eklenen alıntılar, programınızı çalıştırırken bunları yazmayın), giriş işlevi bu dizeyi döndürür ve daha sonra dizeyi yorumlayan dış işleve (eval) aktarılır ve sonucu döndürür (2).

Eval hakkında daha fazla bilgiyi burada bulabilirsiniz .


6

eval(), adından da anlaşılacağı gibi, iletilen argümanı değerlendirir.

raw_input()şimdi input()python 3.x sürümlerinde. Bu nedenle, kullanımının en yaygın örneği python'un 2.x sürümünde sağlanan eval()işlevselliği sağlamaktır input(). raw_input, kullanıcı tarafından girilen verileri bir dize olarak döndürürken, girdi girilen verilerin değerini değerlendirdi ve döndürdü.

eval(input("bla bla"))böylece input()2.x içindeki işlevselliği , yani kullanıcı tarafından girilen verilerin değerlendirilmesini tekrarlar .

Kısacası: eval()kendisine iletilen argümanları değerlendirir ve dolayısıyla eval('1 + 1')2 döndürür .


6

Yararlı uygulamalarından biri eval(), dize içindeki python ifadelerini değerlendirmektir. Örneğin, sözlüğün dosya dizesi temsilinden yükleme:

running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()

Bir değişken olarak okuyun ve düzenleyin:

fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction

Çıktı:

{'Greeting': 'Hello world'}

7
Bu, ne yapacağını soran soruya nasıl cevap veriyor eval?
jkd 28.03.2015

4

Bu soruya cevap vermek için geç kaldım ama kimse soruya net bir cevap vermiyor gibi görünüyor.

Bir kullanıcı sayısal bir değer girerse, input()bir dize döndürür.

>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

Bu nedenle, eval()bir dize olan döndürülen değeri (veya ifadeyi) değerlendirecek ve tamsayı / kayan sayı döndürecektir.

>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>> 
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14

Elbette bu kötü bir uygulamadır. int()veya float()yerine kullanılması gereken eval()bu durumda.

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14

3

Değerlendirme dizesini basit değişmez değerlerle sınırlamak istiyorsanız başka bir seçenek de kullanmaktır ast.literal_eval(). Bazı örnekler:

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}

Gönderen docs :

Bir ifade düğümünü veya Python hazır bilgisi veya kapsayıcı görüntüsü içeren bir dizeyi güvenle değerlendirin. Sağlanan dize veya düğüm yalnızca şu Python değişmez yapılarından oluşabilir: dizeler, baytlar, sayılar, tuples, listeler, dikteler, kümeler, booleans ve Yok.

Bu, güvenilir olmayan kaynaklardan Python değerleri içeren dizeleri, değerleri kendi başına ayrıştırmaya gerek kalmadan güvenle değerlendirmek için kullanılabilir. Öyle değil operatörleri veya indeksleme içeren örneğin, keyfi olarak karmaşık ifadeler değerlendirmek yeteneğine.

Posta listesinden neden bu kadar sınırlı olduğuna gelince :

Değişmez değerlerle operatör ifadelerine izin vermek mümkündür, ancak geçerli uygulamadan çok daha karmaşıktır. Basit bir uygulama güvenli değildir: temelde sınırsız CPU ve bellek kullanımını çaba harcamadan indükleyebilirsiniz ("9 ** 9 ** 9" veya "[Yok] * 9 ** 9" u deneyin).

Yararlılık açısından, bu işlev değişmez değerleri ve kapları repr () ile dizilmiş olarak "yeniden okumak" için yararlıdır. Bu, örneğin JSON'a benzer ancak daha güçlü bir formatta serileştirme için kullanılabilir.


1
ast.literal_eval'1+1'örneğinizin aksine operatörleri desteklemez . Bununla birlikte, listeleri, sayıları, dizeleri vb. Destekler ve bu nedenle yaygın evalkullanım durumları için iyi bir alternatiftir .
benjimin

@benjimin oh haklısın - sadece 1 + 1'i kabul ettiği bir tuhaflık! stackoverflow.com/questions/40584417/…
Brian Burns
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.