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?
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?
Yanıtlar:
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
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.
eval
ve ne yaptığını da yapamaz eval
.
eval
, güvensiz olmaktan başka, yalnızca tek bir ifadeyi değerlendirebildiğinden kod alanı gibi tüm programları çalıştıramaz.
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 os
iç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()
.
eval
olan input()
bir güvenlik delik. input()
Bir değerlendirme ifadesi içine koymayın ve iyi olacaksınız.
eval
birçok durumda bir güvenlik sorunudur.
input
genellikle konsoldan verilerini alır kullanıcı sadece programdan çıkmak ve yazabilirsiniz rm -R *
... neyse
Burada birçok iyi cevap var, ama hiçbiri eval()
onun globals
ve locals
kwarglar 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__'}
builtins
Modü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_count
hala 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 eval
olarak ç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]
Python 2.x input(...)
eşdeğerdir eval(raw_input(...))
, Python 3.x raw_input
yeniden adlandırıldı input
, hangi karışıklığa yol açtığından şüpheleniyorum (muhtemelen input
Python 2.x için belgelere bakıyordunuz ). Ayrıca, eval(input(...))
Python 3.x iyi çalışır, ancak TypeError
Python 2 yükseltir .
Bu durumda eval
, döndürülen dizgiyi input
bir ifadeye zorlamak ve yorumlamak için kullanılır. Genellikle bu kötü uygulama olarak kabul edilir.
input
ne olduğu anlamına geliyor raw_input
.
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.
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 .
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 .
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'}
eval
?
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
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.
ast.literal_eval
'1+1'
örneğinizin aksine operatörleri desteklemez . Bununla birlikte, listeleri, sayıları, dizeleri vb. Destekler ve bu nedenle yaygın eval
kullanım durumları için iyi bir alternatiftir .