问题:定义引发异常的lambda表达式
我如何写一个等于的lambda表达式:
def x():
    raise Exception()不允许以下内容:
y = lambda : raise Exception()回答 0
设置Python皮肤的方法有多种:
y = lambda: (_ for _ in ()).throw(Exception('foobar'))Lambda接受语句。既然raise ex是一条语句,您可以编写一个通用的提升器:
def raise_(ex):
    raise ex
y = lambda: raise_(Exception('foobar'))但是,如果您的目标是避免使用def,则显然不能削减它。但是,它确实允许您有条件地引发异常,例如:
y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))另外,您可以在不定义命名函数的情况下引发异常。您所需要的只是强健的腹部(给定的代码是2.x):
type(lambda:0)(type((lambda:0).func_code)(
  1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())和python3 强健胃部解决方案:
type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())感谢@WarrenSpencer指出了一个非常简单的答案,如果您不在乎引发哪个异常:y = lambda: 1/0。
回答 1
怎么样:
lambda x: exec('raise(Exception(x))')回答 2
实际上,有一种方法,但是它非常人为。
您可以使用compile()内置函数创建代码对象。这使您可以使用raise语句(或其他任何语句),但这又带来了另一个挑战:执行代码对象。通常的方法是使用该exec语句,但这会使您回到最初的问题,即您不能在lambda(或)中执行语句eval()。
解决的办法是破解。诸如lambda语句结果之类的可调用对象均具有属性__code__,该属性实际上可以被替换。因此,如果您创建一个可调用__code__对象并将其值替换为上面的代码对象,则可以得到无需使用语句即可进行评估的内容。但是,实现所有这些都会导致代码非常晦涩:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
上面执行以下操作:
- 该 - compile()调用创建一个引发异常的代码对象;
- 所述 - lambda: 0返回一个可调用什么也不做而返回值0 -这用于以后执行上述代码的对象;
- 在 - lambda x, y, z创建调用函数- __setattr__与剩下的参数,第一个参数的方法,并返回第一个参数!这是必要的,因为- __setattr__它本身会返回- None;
- 在 - map()调用需要的结果- lambda: 0,并使用- lambda x, y, z替换它的- __code__目标与结果- compile()的呼叫。此映射操作的结果是一个包含一个条目的列表,该列表由返回- lambda x, y, z,这就是我们需要这样做的原因- lambda:如果立即使用- __setattr__,将丢失对该- lambda: 0对象的引用!
- 最终, - map()调用返回的列表的第一个(也是唯一一个)元素被执行,导致代码对象被调用,最终引发所需的异常。
它可以工作(在python 2.6中测试),但是绝对不是很漂亮。
最后一点:如果您有权访问该types模块(需要在import之前使用该语句eval),则可以将这段代码缩短一点:使用types.FunctionType()可以创建一个函数来执行给定的代码对象,因此您赢了不需要创建虚拟函数lambda: 0并替换其__code__属性值的技巧。
回答 3
用lambda表单创建的函数不能包含语句。
回答 4
如果您想要的只是引发任意异常的lambda表达式,则可以使用非法表达式来实现。例如,lambda x: [][0]将尝试访问空列表中的第一个元素,这将引发IndexError。
请注意:这是黑客行为,而非功能。请勿在他人可能看到或使用的任何(非代码高尔夫球)代码中使用此代码。
回答 5
我想解释一下Marcelo Cantos提供的答案的UPDATE 3:
type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())说明
lambda: 0是builtins.function该类的一个实例。
type(lambda: 0)是builtins.functionClass。
(lambda: 0).__code__是一个code对象。
甲code对象是保存除了其他方面,编译的字节代码的对象。它在CPython https://github.com/python/cpython/blob/master/Include/include.code中定义。其方法在此处https://github.com/python/cpython/blob/master/Objects/codeobject.c中实现。我们可以在代码对象上运行帮助:
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__)是代码类。
所以当我们说
type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')我们使用以下参数调用代码对象的构造函数:
- argcount = 1
- kwonlyargcount = 0
- nlocals = 1
- stacksize = 1
- 标志= 67
- codestring = b’| \ 0 \ 202 \ 1 \ 0′
- 常数=()
- 名称=()
- varnames =(’x’,)
- 文件名=”
- 名称=”
- firstlineno = 1
- lnotab = b”
您可以在PyCodeObject
https://github.com/python/cpython/blob/master/Include/include.code的定义中了解自变量的含义。flags例如,参数的值67CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE。
最多importand参数是,codestring其中包含指令操作码。让我们看看它们的含义。
>>> import dis
>>> dis.dis(b'|\0\202\1\0')
          0 LOAD_FAST                0 (0)
          2 RAISE_VARARGS            1
          4 <0>可以在以下网址找到操作码的文档:
 https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions。第一个字节是的操作码LOAD_FAST,第二个字节是其参数,即0。
LOAD_FAST(var_num)
    Pushes a reference to the local co_varnames[var_num] onto the stack.因此,我们将引用x推入堆栈。的varnames是只含有“X”的字符串列表。我们将把要定义的函数的唯一参数推入堆栈。
下一个字节是其操作码,RAISE_VARARGS下一个字节是其参数,即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)TOS是堆栈的顶部。由于我们将x函数的第一个参数()推入了堆栈且argc为1,因此x如果它是异常实例,
 则将其x引发,否则将其引发。
最后节即0不被使用。这不是有效的操作码。它可能不在那里。
回到代码片段,我们在分析:
type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())我们称为代码对象的构造函数:
type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')我们将代码对象和空字典传递给函数对象的构造函数:
type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)让我们在函数对象上调用help来了解参数的含义。
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然后,我们调用传递的Exception实例作为参数的构造函数。因此,我们调用了引发异常的lambda函数。让我们运行代码段,看看它确实按预期工作。
>>> 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改进措施
我们看到字节码的最后节是无用的。让我们不要轻易将这个复杂的表达式弄乱。让我们删除该字节。另外,如果我们想打高尔夫球,我们可以省略Exception的实例化,而是将Exception类作为参数传递。这些更改将导致以下代码:
type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1',(),(),('x',),'','',1,b''),{}
)(Exception)当我们运行它时,我们将获得与以前相同的结果。它只是更短。

