Python kodunun dinamik değerlendirmesine bakıyordum ve eval()ve compile()fonksiyonları ve execifadesiyle karşılaştım.
Birisi evalve arasındaki farkı ve execfarklı compile()uyum biçimlerini nasıl açıklayabilir ?
Python kodunun dinamik değerlendirmesine bakıyordum ve eval()ve compile()fonksiyonları ve execifadesiyle karşılaştım.
Birisi evalve arasındaki farkı ve execfarklı compile()uyum biçimlerini nasıl açıklayabilir ?
Yanıtlar:
Temel olarak, evaliçin kullanılan Eval uate tek dinamik olarak oluşturulmuş Python ekspresyonu ve execiçin kullanılan exec dinamik olarak sadece yan etkileri için Python kodu oluşturulur ute.
evalve execşu iki farklılığa sahip olun:
evalSadece kabul tek bir ifade , execdöngüler,: Python ifadeleri olan bir kod bloğu alabilir try: except:, classve fonksiyonu / yöntem defbö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 execdö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 execbir deyim vardı, çünkü CPython'un fonksiyon execiçindeki yan etkileri için kullanılan fonksiyonlar için farklı bir kod nesnesi üretmesi gerekiyordu .
Python 3'te execbir 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 Noneoysa '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 evalbir dize geçirilen eğer fonksiyonu), compilekaynak 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 execya eval, onlar aynı şekilde davranır gerçeği hariç, exechala dönen, dönüş değeri yok sayar Nonehep. Bu nedenle, evaldeyim içeren bir şeyi yürütmek mümkündür , eğer compiledize 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 evalbir dize geçirilen eğer fonksiyonu), compilekaynak 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 evalexec(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
>>>
evalFonksiyon 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
execve evalher ikisi de bir program / ifadesi olarak çalıştırılabilir kabul str, unicodeya da bytesya da bir şekilde, bir nesne içeren kaynak kodu codenesne Python bayt kodu ihtiva eder.
Bir str/ unicode/ bytesiçeren kaynak kodu iletildiyse exec, aşağıdakilere eşit davranır:
exec(compile(source, '<string>', 'exec'))
ve evalbenzer ş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 ExprPython soyut dilbilgisinde düğümler denir ; tam tersi doğru değildir), execdönüş değerine ihtiyacınız yoksa her zaman kullanabilirsiniz . Yani, ya eval('my_func(42)')da exec('my_func(42)'), evaldöndürülen değeri döndüren my_funcve execonu 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 execifadeler 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 execve eval- 2 ek pozisyonel argümanları kabul globalsve 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 execveya eval, ancak herhangi bir sözlük kullanılabilir için globalsve herhangi mappingiçin locals(dahil dicttabii ki). Bunlar yalnızca kodun gördüğü değişkenleri kısıtlamak / değiştirmek için değil, aynı zamanda executed 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 execve evalyerleşik ins olarak modülü eklemek __builtins__eksik mesajı otomatik olarak globaller kadar).
Python 2'de, resmi sözdizimi execaçıklamada aslında exec code in globals, localsolduğ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).
compilecompile(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 execya da evalbir içine kaynağını derleyerek codeönceden nesne. modeParametre, kod parçası tür compilefonksiyonu 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- elsezinciri, bir döngü elseve trykendi diliyle except, elseve finallybloklar 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ı execve evalaslı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 compilederlenmesini 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.codeast.parsecompile(source, filename, mode, PyCF_ONLY_AST)
evalYalnızca tek bir ifade içeren bir dizeyi değerlendirmenize izin verirken , bir deyimin evaltamamını veya compilebayt koduna dönüştürülmüş bir modülü bile yapabilirsiniz ; yani, Python 2 printile bir deyimdir ve evaldoğ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
compileonunla 'exec'bir içine modunda codenesne ve mümkün eval it ; evalfonksiyon 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 evalve execbakarsanız, bu çok belirgindir; her ikisi de PyEval_EvalCodeaynı argümanlarla çağrılır, tek fark execaçıkça dönendirNone .
execPython 2 ve Python 3 arasındaki sözdizimi farklarıPython önemli farklardan biri 2'ye yani execbir ifadedir ve evalbir dahili fonksiyonu (hem yerleşik işlevler Python 3'te). execPython 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 , execCPython 2'de bildiride de bu sözdizimi ile kullanılabilir görünüyor aynen böyle execPython 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 code2 veya 3 uzunluğunda bir demetse globalsve aksi takdirde ifadeye localsgeçirilmemişse exec, codeyorumlanacaktı sanki tupleın 2. ve 3. elementleri sırasıyla globalsve localsidi. 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şlevexecolan 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 execPython 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 execbir 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 execzaman 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 ( forbunun yerine bir döngü kullanın!).
42aynı 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 = csağ tarafı gibi mükemmel b = cbir ifadedir - ki bu bir ifade değildir.
execbir 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.evalifadeyi 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.compiledaha düşük seviyeli bir versiyonudur execve 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 execmodunda ama ilk açıklamada hariç her şeyi göz ardı eder. Sonuçları içeren bir if/ elseifadesinin tek bir ifade olarak kabul edildiğini unutmayın.exec()şimdi aslında bir işlev.
exechedeflediğiniz sürümde bir ifade olduğundan, bu ebeveynleri dahil etmek aldatıcıdır ve eğer in globals, localsbuggy 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 ?