Python kodunun dinamik değerlendirmesine bakıyordum ve eval()
ve compile()
fonksiyonları ve exec
ifadesiyle karşılaştım.
Birisi eval
ve arasındaki farkı ve exec
farklı compile()
uyum biçimlerini nasıl açıklayabilir ?
Python kodunun dinamik değerlendirmesine bakıyordum ve eval()
ve compile()
fonksiyonları ve exec
ifadesiyle karşılaştım.
Birisi eval
ve arasındaki farkı ve exec
farklı compile()
uyum biçimlerini nasıl açıklayabilir ?
Yanıtlar:
Temel olarak, eval
için kullanılan Eval uate tek dinamik olarak oluşturulmuş Python ekspresyonu ve exec
için kullanılan exec dinamik olarak sadece yan etkileri için Python kodu oluşturulur ute.
eval
ve exec
şu iki farklılığa sahip olun:
eval
Sadece kabul tek bir ifade , exec
döngüler,: Python ifadeleri olan bir kod bloğu alabilir try: except:
, class
ve fonksiyonu / yöntem def
böyle devam initions ve.
Python'daki bir ifade, değişken atamada değer olarak sahip olabileceğiniz her şeydir:
a_variable = (anything you can put within these parentheses is an expression)
eval
verilen ifadenin değerini döndürür , oysa exec
dönüş değerini kodundan yok sayar ve her zaman döndürür None
(Python 2'de bir deyimdir ve bir ifade olarak kullanılamaz, bu yüzden gerçekten hiçbir şey döndürmez).
1.0 - 2.7 sürümlerinde exec
bir deyim vardı, çünkü CPython'un fonksiyon exec
içindeki yan etkileri için kullanılan fonksiyonlar için farklı bir kod nesnesi üretmesi gerekiyordu .
Python 3'te exec
bir fonksiyondur; kullanımının, kullanıldığı işlevin derlenmiş bayt kodu üzerinde hiçbir etkisi yoktur.
Temel olarak:
>>> a = 5
>>> eval('37 + a') # it is an expression
42
>>> exec('37 + a') # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47') # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47') # you cannot evaluate a statement
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a = 47
^
SyntaxError: invalid syntax
compile
İçinde 'exec'
modunda bir bayt içine tabloların herhangi bir sayıda derler dolaylı olarak zaman döner None
oysa 'eval'
modunda bir derler tek bayt içine ekspresyonu bu döner bu ifadenin değeri.
>>> eval(compile('42', '<string>', 'exec')) # code returns None
>>> eval(compile('42', '<string>', 'eval')) # code returns 42
42
>>> exec(compile('42', '<string>', 'eval')) # code returns 42,
>>> # but ignored by exec
In 'eval'
(ve dolayısıyla modunda eval
bir dize geçirilen eğer fonksiyonu), compile
kaynak kod ifadeleri veya tek ifadesi ötesinde başka bir şey içeriyorsa bir istisna yükseltir:
>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
Aslında "eval sadece tek bir ifadeyi kabul eder" ifadesi yalnızca bir dize (Python kaynak kodu içeren ) iletildiğinde geçerlidir eval
. Sonra dahili bytecode kullanarak derlenir compile(source, '<string>', 'eval')
Bu fark gerçekten geliyor.
Bir ederse code
(Python içeren nesne bayt kodu ) geçirilir exec
ya eval
, onlar aynı şekilde davranır gerçeği hariç, exec
hala dönen, dönüş değeri yok sayar None
hep. Bu nedenle, eval
deyim içeren bir şeyi yürütmek mümkündür , eğer compile
dize olarak geçirmek yerine bayt koduna girerseniz :
>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
derlenmiş kod deyimleri içermesine rağmen sorunsuz çalışır. Yine de döner None
, çünkü bu, döndürülen kod nesnesinin dönüş değeridir compile
.
In 'eval'
(ve dolayısıyla modunda eval
bir dize geçirilen eğer fonksiyonu), compile
kaynak kod ifadeleri veya tek ifadesi ötesinde başka bir şey içeriyorsa bir istisna yükseltir:
>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
exec
ve eval
exec
(Oldu fonksiyonu Python 2'de bir açıklama ) dinamik olarak oluşturulan bir açıklama veya programı çalıştırmak için kullanılır:
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
eval
Fonksiyon için aynısını yapar tek bir ifade , ve ifade değerini verir:
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
exec
ve eval
her ikisi de bir program / ifadesi olarak çalıştırılabilir kabul str
, unicode
ya da bytes
ya da bir şekilde, bir nesne içeren kaynak kodu code
nesne Python bayt kodu ihtiva eder.
Bir str
/ unicode
/ bytes
içeren kaynak kodu iletildiyse exec
, aşağıdakilere eşit davranır:
exec(compile(source, '<string>', 'exec'))
ve eval
benzer şekilde aşağıdakilere eşdeğer davranır:
eval(compile(source, '<string>', 'eval'))
Tüm ifadeler Python'da ifadeler olarak kullanılabileceğinden (bunlara Expr
Python soyut dilbilgisinde düğümler denir ; tam tersi doğru değildir), exec
dönüş değerine ihtiyacınız yoksa her zaman kullanabilirsiniz . Yani, ya eval('my_func(42)')
da exec('my_func(42)')
, eval
döndürülen değeri döndüren my_func
ve exec
onu atayan farkı kullanabilirsiniz :
>>> def my_func(arg):
... print("Called with %d" % arg)
... return arg * 2
...
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>>
2 arasından yalnızca exec
ifadeler içermektedir kaynak kodu kabul gibi def
, for
, while
, import
, veya class
, atama deyimi (aka a = 42
), veya tüm programlar:
>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
Hem exec
ve eval
- 2 ek pozisyonel argümanları kabul globals
ve locals
- küresel ve yerel değişken kod gördüğünü kapsamları vardır. İçin Bu varsayılan globals()
ve locals()
denilen o kapsamındaki exec
veya eval
, ancak herhangi bir sözlük kullanılabilir için globals
ve herhangi mapping
için locals
(dahil dict
tabii ki). Bunlar yalnızca kodun gördüğü değişkenleri kısıtlamak / değiştirmek için değil, aynı zamanda exec
uted kodun oluşturduğu değişkenleri yakalamak için de kullanılır :
>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}
(Eğer bütün şeklinde değerini görüntülemek varsa g
, bunun nedeni, çok daha uzun olacaktır exec
ve eval
yerleşik ins olarak modülü eklemek __builtins__
eksik mesajı otomatik olarak globaller kadar).
Python 2'de, resmi sözdizimi exec
açıklamada aslında exec code in globals, locals
olduğu gibi,
>>> exec 'global a; a, b = 123, 42' in g, l
Ancak alternatif sözdizimi exec(code, globals, locals)
de her zaman kabul edilmiştir (aşağıya bakınız).
compile
compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
Yerleşik ile aynı kod tekrarlanan çağırmaları hızlandırmak için kullanılabilir exec
ya da eval
bir içine kaynağını derleyerek code
önceden nesne. mode
Parametre, kod parçası tür compile
fonksiyonu kabul eder ve ürettiği bayt kodu tür. Seçimler 'eval'
, 'exec'
ve 'single'
:
'eval'
modu tek bir ifade bekler ve çalıştırıldığında o ifadenin değerini döndürecek bayt kodu üretir :
>>> dis.dis(compile('a + b', '<string>', 'eval'))
1 0 LOAD_NAME 0 (a)
3 LOAD_NAME 1 (b)
6 BINARY_ADD
7 RETURN_VALUE
'exec'
tek ifadelerden tüm kod modüllerine kadar her türlü python yapısını kabul eder ve bunları modül üst düzey ifadeleriymiş gibi yürütür. Kod nesnesi şunu döndürür None
:
>>> dis.dis(compile('a + b', '<string>', 'exec'))
1 0 LOAD_NAME 0 (a)
3 LOAD_NAME 1 (b)
6 BINARY_ADD
7 POP_TOP <- discard result
8 LOAD_CONST 0 (None) <- load None on stack
11 RETURN_VALUE <- return top of stack
'single'
son ifade bir ifade deyimi ise, tek bir deyim 'exec'
içeren (veya birbirinden ayrılmış birden çok deyim ) bir kaynak kodunu kabul eden sınırlı bir biçimdir , sonuçta elde edilen bayt kodu da bu ifadenin değerini standart çıktıya (!) yazdırır .;
repr
Bir if
- elif
- else
zinciri, bir döngü else
ve try
kendi diliyle except
, else
ve finally
bloklar tek deyim olarak kabul edilir.
2 üst düzey ifade içeren bir kaynak parçası için bir hatadır 'single'
, ancak Python 2'de bazen kodda birden fazla üst düzey ifadeye izin veren bir hata vardır; sadece ilki derlenir; geri kalanı yok sayılır:
Python 2.7.8'de:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5
Ve Python 3.4.2'de:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a = 5
^
SyntaxError: multiple statements found while compiling a single statement
Bu etkileşimli Python kabukları yapmak için çok yararlıdır. Ancak, ortaya çıkan kod olsa bile , ifadenin değeri döndürülmezeval
.
Böylece fonksiyon ve modlarından en büyük ayrımı exec
ve eval
aslında gelir compile
.
Kaynak kodu bayt koduna derlemeye ek olarak, soyut sözdizimi ağaçlarının (Python kodunun ayrıştırma ağaçları) nesnelere compile
derlenmesini destekler ; ve soyut sözdizimi ağaçlarına kaynak kodu ( Python ile yazılmış ve sadece çağrılar ); bunlar, örneğin kaynak kodunu anında değiştirmek ve ayrıca dinamik kod oluşturmak için kullanılır, çünkü kodu karmaşık durumlarda metin satırları yerine bir düğüm ağacı olarak işlemek daha kolaydır.code
ast.parse
compile(source, filename, mode, PyCF_ONLY_AST)
eval
Yalnızca tek bir ifade içeren bir dizeyi değerlendirmenize izin verirken , bir deyimin eval
tamamını veya compile
bayt koduna dönüştürülmüş bir modülü bile yapabilirsiniz ; yani, Python 2 print
ile bir deyimdir ve eval
doğrudan yönlendirilemez:
>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print("Python is cool")
^
SyntaxError: invalid syntax
compile
onunla 'exec'
bir içine modunda code
nesne ve mümkün eval
it ; eval
fonksiyon dönecektir None
.
>>> code = compile('for i in range(3): print("Python is cool")',
'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool
CPython 3'te kaynak koduna bakar eval
ve exec
bakarsanız, bu çok belirgindir; her ikisi de PyEval_EvalCode
aynı argümanlarla çağrılır, tek fark exec
açıkça dönendirNone
.
exec
Python 2 ve Python 3 arasındaki sözdizimi farklarıPython önemli farklardan biri 2'ye yani exec
bir ifadedir ve eval
bir dahili fonksiyonu (hem yerleşik işlevler Python 3'te). exec
Python 2'nin resmi sözdiziminin olduğu bilinen bir gerçektir exec code [in globals[, locals]]
.
Python 2-to-3 çoğunluğunun aksine süpürmede kılavuzları görünüyor önermek , exec
CPython 2'de bildiride de bu sözdizimi ile kullanılabilir görünüyor aynen böyle exec
Python 3'te işlev çağırma Python 0.9.9 yapmamızın en önemli sebebi olduğunu exec(code, globals, locals)
yerleüik işlevde! Ve bu yerleşik işlev, Python 1.0 sürümünden önce bir yerdeexec
ifadeyle değiştirildi .
Python 0.9.9 ile geriye dönük uyumluluğun kırılmaması arzu edildiğinden, Guido van Rossum 1993'te bir uyumluluk kesmek ekledi : eğer code
2 veya 3 uzunluğunda bir demetse globals
ve aksi takdirde ifadeye locals
geçirilmemişse exec
, code
yorumlanacaktı sanki tupleın 2. ve 3. elementleri sırasıyla globals
ve locals
idi. Uyumluluk kesmek Python 1.4 belgelerinde (çevrimiçi olarak en eski sürüm) bile bahsedilmedi ; ve böylece, Kasım 2012'de tekrar belgelenene kadar, taşıma kılavuzlarının ve araçlarının birçok yazarları tarafından bilinmiyordu :
Birinci ifade ayrıca 2 veya 3 uzunluğunda bir demet de olabilir. Bu durumda, isteğe bağlı parçalar atlanmalıdır. Form
exec(expr, globals)
eşittirexec expr in globals
, formexec(expr, globals, locals)
eşittirexec expr in globals, locals
. Tuple biçimi , bir deyimden ziyade bir işlevexec
olan Python 3 ile uyumluluk sağlarexec
.
Evet, CPython 2.7'de, yirmi yıldır geriye dönük uyumluluk için orada bulunduğunda, ileriye dönük bir uyumluluk seçeneği olarak adlandırılan (neden insanları geriye dönük bir uyumluluk seçeneği olduğu konusunda karıştırmayın) .
Böylece ise exec
Python 1 ve Python 2'de bir ifadesidir ve bu yerleşik bir işleve Python 3 ve Python 0.9.9 yılında,
>>> exec("print(a)", globals(), {'a': 42})
42
muhtemelen yayınlanmış her Python sürümünde aynı davranışlara sahipti; ve Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) ve IronPython 2.6.1'de de çalışır (CPython'un belgelenmemiş davranışını yakından izleyen onlara şuralar).
Uyumluluk kesmek ile Pythons 1.0 - 2.7'de yapamayacağınız şey, dönüş değerini exec
bir değişkene depolamaktır :
Python 2.7.11+ (default, Apr 17 2016, 14:00:29)
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
File "<stdin>", line 1
a = exec('print(42)')
^
SyntaxError: invalid syntax
(Python 3'te her exec
zaman olduğu gibi yararlı olmaz None
) veya şunlara bir referans iletin exec
:
>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
Olasılıkla birisinin aslında kullandığı bir kalıp;
Veya liste kavrayışında kullanın:
>>> [exec(i) for i in ['print(42)', 'print(foo)']
File "<stdin>", line 1
[exec(i) for i in ['print(42)', 'print(foo)']
^
SyntaxError: invalid syntax
Bu, liste kavramalarının kötüye kullanılmasıdır ( for
bunun yerine bir döngü kullanın!).
42
aynı zamanda bir ifadedir ve bunu @
dekoratör olarak kullanamazsınız .
decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE
; başka bir deyişle, dekoratörler, SADECE bir (belki noktalı) tanımlayıcı ve ardından isteğe bağlı çağrı bağımsız değişkenleri olarak rastgele ifadeler kullanamazsınız.
a = b = c
sağ tarafı gibi mükemmel b = c
bir ifadedir - ki bu bir ifade değildir.
exec
bir ifade değildir: Python 2.x'de bir ifade ve Python 3.x'te bir işlev. Bir dizede bulunan bir deyimi veya deyim kümesini derler ve derhal değerlendirir. Misal:
exec('print(5)') # prints 5.
# exec 'print 5' if you use Python 2.x, nor the exec neither the print is a function there
exec('print(5)\nprint(6)') # prints 5{newline}6.
exec('if True: print(6)') # prints 6.
exec('5') # does nothing and returns nothing.
eval
ifadeyi değerlendiren ve ifadenin ürettiği değeri döndüren yerleşik bir işlevdir ( deyim değil ). Misal:
x = eval('5') # x <- 5
x = eval('%d + 6' % x) # x <- 11
x = eval('abs(%d)' % -100) # x <- 100
x = eval('x = 5') # INVALID; assignment is not an expression.
x = eval('if 1: x = 4') # INVALID; if is a statement, not an expression.
compile
daha düşük seviyeli bir versiyonudur exec
ve eval
. İfadelerinizi veya ifadelerinizi yürütmez veya değerlendirmez, ancak bunu yapabilen bir kod nesnesi döndürür. Modlar aşağıdaki gibidir:
compile(string, '', 'eval')
yapmış olsaydınız yürütülecek olan kod nesnesini döndürür eval(string)
. O Not olamaz bu modda ifadeleri kullanmak; yalnızca (tek) bir ifade geçerlidir.compile(string, '', 'exec')
yapmış olsaydınız yürütülecek olan kod nesnesini döndürür exec(string)
. Burada istediğiniz sayıda ifadeyi kullanabilirsiniz.compile(string, '', 'single')
gibidir exec
modunda ama ilk açıklamada hariç her şeyi göz ardı eder. Sonuçları içeren bir if
/ else
ifadesinin tek bir ifade olarak kabul edildiğini unutmayın.exec()
şimdi aslında bir işlev.
exec
hedeflediğiniz sürümde bir ifade olduğundan, bu ebeveynleri dahil etmek aldatıcıdır ve eğer in globals, locals
buggy de kullanmaya çalışırsanız .
exec
parantezleri destekler ve Python 2'de çağırma gibi işlev görür .
x = (y)
, bu doğru olabilir. Başka bir ifade-döndü-fonksiyonu print
; print(1, 2, 3)
python 2 ve 3'teki sonuçları karşılaştırın .
exec ifade içindir ve hiçbir şey döndürmez. eval ifadedir ve ifadenin değerini döndürür.
ifade "bir şey" anlamına gelirken ifade "bir şey yap" anlamına gelir.
[i for i in globals().values() if hasattr(i, '__call__')][0]
Bir ifade veya ifade miydi ? Bir ifade olsaydı, neden@
dekoratör olarak kullanamıyorum ?