Eşdeğer bir lambda ifadesini nasıl yazabilirim:
def x():
raise Exception()
Aşağıdakilere izin verilmez:
y = lambda : raise Exception()
y=lambda...üzerine def y:o zaman?
Eşdeğer bir lambda ifadesini nasıl yazabilirim:
def x():
raise Exception()
Aşağıdakilere izin verilmez:
y = lambda : raise Exception()
y=lambda...üzerine def y:o zaman?
Yanıtlar:
Bir Python'u kaplamanın birden fazla yolu vardır:
y = lambda: (_ for _ in ()).throw(Exception('foobar'))
Lambdas ifadeleri kabul eder. Yana raise exbir ifadedir, bir genel amaçlı yükselten yazabiliriz:
def raise_(ex):
raise ex
y = lambda: raise_(Exception('foobar'))
Ancak hedefiniz a'dan kaçınmaksa def, bu kesinlikle onu kesmez. Bununla birlikte, istisnaları koşullu olarak yükseltmenize izin verir, örneğin:
y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))
Alternatif olarak, adlandırılmış bir işlevi tanımlamadan bir istisna oluşturabilirsiniz. Tek ihtiyacınız olan güçlü bir mide (ve verilen kod için 2.x):
type(lambda:0)(type((lambda:0).func_code)(
1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())
Ve bir python3 güçlü mide çözeltisi:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Eğer yükseltilir hangi istisna umursamazsak çok basit bir cevabı işaret için teşekkürler @WarrenSpencer: y = lambda: 1/0.
lambda: 1 / 0. Sonunda, düzenli bir istisna yerine bir ZeroDivisionError atılacaksınız. İstisnanın yayılmasına izin verilirse, bir grup ZeroDivisionErrors görmeye başlamak için kodunuzda hata ayıklayan birine garip gelebileceğini unutmayın.
y = 1/0İstisna türü ilgisiz ise süper akıllı bir çözümdür
Nasıl olur:
lambda x: exec('raise(Exception(x))')
SyntaxError, Python 2.7.11'de bir tane alıyorum.
Aslında, bir yol var, ama çok çelişkili.
Yerleşik işlevi kullanarak bir kod nesnesi oluşturabilirsiniz compile(). Bu, raiseifadeyi (veya bu konuda başka bir ifadeyi) kullanmanıza izin verir , ancak başka bir zorluğu ortaya çıkarır: kod nesnesini yürütme. Her zamanki yol execifadeyi kullanmak olacaktır , ancak bu sizi orijinal soruna geri götürür, yani bir lambda(veya eval()bu konuda) ifadelerini çalıştıramazsınız .
Çözüm bir hack'tir. Bir lambdaifadenin sonucu gibi callables __code__, aslında değiştirilebilen bir özelliğe sahiptir. Bu nedenle, bir çağrılabilir oluşturursanız ve __code__değerini yukarıdaki kod nesnesiyle değiştirirseniz , ifadeleri kullanmadan değerlendirilebilecek bir şey elde edersiniz. Bununla birlikte, tüm bunları başarmak çok belirsiz bir kodla sonuçlanır:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Yukarıdakiler şunları yapar:
compile()Çağrı özel durum bir kod nesnesi oluşturur;
lambda: 0döner bir şey yapmaz ama değeri 0 döndürür bir çağrılabilir - Bu sonradan Yukarıdaki kod nesnesini çalıştırmak için kullanılır;
Bu , kalan argümanlarla ilk argüman yöntemini lambda x, y, zçağıran bir işlev oluşturur __setattr__ve İLK ARGUMENTİ DÖNDÜRÜR! Bu gereklidir, çünkü __setattr__kendisi geri döner None;
map()Çağrı sonucunu alır lambda: 0ve kullanma lambda x, y, zyerine geçer 's __code__sonucu ile nesne compile()çağrısı. Bu harita işleminin sonucu, bir girişi olan, bir girişi döndürülen bir listedir lambda x, y, z, bu yüzden buna ihtiyacımız vardır lambda: __setattr__hemen kullanırsak, lambda: 0nesneye olan referansı kaybederiz !
son olarak, map()çağrı tarafından döndürülen listenin ilk (ve yalnızca) öğesi yürütülür ve kod nesnesinin çağrılmasıyla sonuçlanır ve sonuçta istenen istisna yükselir.
Çalışıyor (Python 2.6'da test edildi), ancak kesinlikle hoş değil.
Son bir not: typesmodüle erişiminiz varsa ( importifadeyi kendinizden önce kullanmanız gerekir eval), bu kodu biraz kısaltabilirsiniz: kullanarak types.FunctionType(), verilen kod nesnesini yürütecek bir işlev oluşturabilirsiniz, böylece kukla bir işlev yaratma lambda: 0ve __code__özniteliğinin değerini değiştirme hackine ihtiyaç duymaz .
Lambda formlarıyla oluşturulan işlevler ifade içeremez .
İstediğiniz tek şey, keyfi bir istisna oluşturan bir lambda ifadesi ise, bunu yasadışı bir ifadeyle yapabilirsiniz. Örneğin, lambda x: [][0]boş bir listedeki ilk öğeye erişmeye çalışır ve bu da bir IndexError hatası oluşturur.
LÜTFEN DİKKAT : Bu bir hack, bir özellik değil. Bunu , başka bir insanın görebileceği veya kullanabileceği herhangi bir (kodsuz golf) kodda kullanmayın.
TypeError: <lambda>() takes exactly 1 positional argument (2 given). IndexError'dan emin misiniz?
lambda *x: [][0]. (Orijinal sürüm sadece bir argüman alır; hiçbir argüman lambda : [][0]için kullanmayın ; iki için kullanın lambda x,y: [][0]; vb.)
lambda x: {}["I want to show this message. Called with: %s" % x] Üretiyor:KeyError: 'I want to show this message. Called with: foo'
Marcelo Cantos tarafından verilen cevabın GÜNCELLEME 3'ü hakkında bir açıklama yapmak istiyorum :
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
lambda: 0builtins.functionsınıfın bir örneğidir .
type(lambda: 0)olduğu builtins.functionsınıf.
(lambda: 0).__code__bir codenesnedir.
Bir codenesnenin diğer şeylerin yanı sıra, derlenmiş bayt kodu tutan bir nesnedir. Burada CPython https://github.com/python/cpython/blob/master/Include/code.h . Yöntemleri burada uygulanır https://github.com/python/cpython/blob/master/Objects/codeobject.c . Kod nesnesinde yardımı çalıştırabiliriz:
Help on code object:
class code(object)
| code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
| constants, names, varnames, filename, name, firstlineno,
| lnotab[, freevars[, cellvars]])
|
| Create a code object. Not for the faint of heart.
type((lambda: 0).__code__)kod sınıfıdır.
Yani dediğimizde
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
kod nesnesinin yapıcısını aşağıdaki bağımsız değişkenlerle çağırıyoruz:
PyCodeObject
Https://github.com/python/cpython/blob/master/Include/code.h tanımında argümanların ne anlama geldiğini okuyabilirsiniz . Bağımsız flagsdeğişken için 67 değeri örneğinCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE .
En önemli argüman, codestringtalimat opcodlarını içeren argümandır . Ne anlama geldiğini görelim.
>>> import dis
>>> dis.dis(b'|\0\202\1\0')
0 LOAD_FAST 0 (0)
2 RAISE_VARARGS 1
4 <0>
Opcodların belgelerini burada bulabilirsiniz
https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . İlk bayt opcode LOAD_FAST, ikinci bayt argümanı yani 0.
LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.
Bu yüzden referansı xyığının üzerine itiyoruz. varnamesSadece 'x' içeren dizeler bir listedir. Tanımladığımız işlevin tek argümanını yığına iteceğiz.
Sonraki bayt opcode'dur RAISE_VARARGSve sonraki bayt argümanıdır yani 1.
RAISE_VARARGS(argc)
Raises an exception using one of the 3 forms of the raise statement, depending on the value of argc:
0: raise (re-raise previous exception)
1: raise TOS (raise exception instance or type at TOS)
2: raise TOS1 from TOS (raise exception instance or type at TOS1 with __cause__ set to TOS)
Hizmet Şartları, yığının en tepesidir. İşlevimizin ilk argümanını ( x) yığına ittiğimizden ve argc1 olduğu için,
xbunun bir istisna örneği olup olmadığını veya başka bir örneğini oluşturup xyükseltiriz.
Son bayt yani 0 kullanılmaz. Geçerli bir opcode değil. Orada olmayabilir de.
Kod pasajına geri dönersek, herhangi bir şey yapıyoruz:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Kod nesnesinin yapıcısını çağırdık:
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
Kod nesnesini ve boş bir sözlüğü bir işlev nesnesinin yapıcısına iletiriz:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)
Bağımsız değişkenlerin ne anlama geldiğini görmek için bir işlev nesnesine yardım diyelim.
Help on class function in module builtins:
class function(object)
| function(code, globals, name=None, argdefs=None, closure=None)
|
| Create a function object.
|
| code
| a code object
| globals
| the globals dictionary
| name
| a string that overrides the name from the code object
| argdefs
| a tuple that specifies the default argument values
| closure
| a tuple that supplies the bindings for free variables
Daha sonra argüman olarak bir İstisna örneğini geçiren inşa edilmiş işlevi çağırırız. Sonuç olarak bir istisna yaratan lambda fonksiyonunu aradık. Parçacığı çalıştıralım ve gerçekten de amaçlandığı gibi çalıştığını görelim.
>>> type(lambda: 0)(type((lambda: 0).__code__)(
... 1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
... )(Exception())
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "", line 1, in
Exception
Bayt kodunun son baytının yararsız olduğunu gördük. Bu karmaşık ifadeyi iğrenç bir şekilde karıştırmayalım. Hadi şu baytı kaldıralım. Ayrıca biraz golf oynamak istiyorsak, İstisna örneğini atlayabilir ve bunun yerine İstisna sınıfını bir argüman olarak geçebiliriz. Bu değişiklikler aşağıdaki kodla sonuçlanır:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1',(),(),('x',),'','',1,b''),{}
)(Exception)
Çalıştırdığımızda, önceki ile aynı sonucu elde ederiz. Sadece daha kısa.