我怎么能写一个相当于的lambda表达式:
def x():
raise Exception()
Run Code Online (Sandbox Code Playgroud)
以下是不允许的:
y = lambda : raise Exception()
Run Code Online (Sandbox Code Playgroud)
Mar*_*tos 140
更新2:我错了!事实证明,为Python设置皮肤的方法不止一种:
y = lambda: (_ for _ in ()).throw(Exception('foobar'))
Run Code Online (Sandbox Code Playgroud)
不,Lambdas只接受表达式. raise ex是一份声明.当然,你可以写一个通用的提升者:
def raise_(ex):
raise ex
y = lambda: raise_(Exception('foobar'))
Run Code Online (Sandbox Code Playgroud)
但如果你的目标是避免a def,这显然不会削减它.但它允许您有条件地引发异常,例如:
y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))
Run Code Online (Sandbox Code Playgroud)
更新:好的,所以你可以在不定义命名函数的情况下引发异常.你需要的只是一个强大的胃(和给定代码的2.x):
type(lambda:0)(type((lambda:0).func_code)(
1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())
Run Code Online (Sandbox Code Playgroud)
更新3:和python3 强胃解决方案:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Run Code Online (Sandbox Code Playgroud)
更新4:感谢@WarrenSpencer指出一个非常简单的答案,如果你不关心引发的异常:y = lambda: 1/0.
vvk*_*wss 50
怎么样:
lambda x: exec('raise(Exception(x))')
Run Code Online (Sandbox Code Playgroud)
kat*_*tsu 17
我想解释一下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())
Run Code Online (Sandbox Code Playgroud)
lambda: 0是builtins.function类的一个实例。
type(lambda: 0)是builtins.function班级。
(lambda: 0).__code__是一个code对象。
甲code对象是保存除了其他方面,编译的字节代码的对象。它在 CPython https://github.com/python/cpython/blob/master/Include/code.h 中定义。它的方法在这里实现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.
Run Code Online (Sandbox Code Playgroud)
type((lambda: 0).__code__)是代码类。
所以当我们说
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.
Run Code Online (Sandbox Code Playgroud)
我们使用以下参数调用代码对象的构造函数:
您可以在PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h的定义中了解参数的含义。flags例如,参数的值 67是CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE。
最重要的参数是codestring包含指令操作码的 。让我们看看它们是什么意思。
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
Run Code Online (Sandbox Code Playgroud)
操作码的文档可以在这里找到
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.
Run Code Online (Sandbox Code Playgroud)
所以我们将引用压入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)
Run Code Online (Sandbox Code Playgroud)
TOS 是栈顶的。由于我们将x函数的第一个参数 ( )压入堆栈并且argc为 1,因此x如果它是异常实例,我们将引发 ,
或者创建一个实例,x否则将引发它。
不使用最后一个字节,即 0。它不是有效的操作码。它也可能不在那里。
回到我们正在分析的代码片段:
>>> import dis
>>> dis.dis(b'|\0\202\1\0')
0 LOAD_FAST 0 (0)
2 RAISE_VARARGS 1
4 <0>
Run Code Online (Sandbox Code Playgroud)
我们调用了代码对象的构造函数:
LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.
Run Code Online (Sandbox Code Playgroud)
我们将代码对象和一个空字典传递给函数对象的构造函数:
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)
Run Code Online (Sandbox Code Playgroud)
让我们在函数对象上调用 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
Run Code Online (Sandbox Code Playgroud)
然后我们调用构造函数,传递一个 Exception 实例作为参数。因此,我们调用了一个引发异常的 lambda 函数。让我们运行代码片段,看看它确实按预期工作。
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Run Code Online (Sandbox Code Playgroud)
我们看到字节码的最后一个字节是无用的。让我们不要把这个复杂的表达方式杂乱无章。让我们删除那个字节。此外,如果我们想打高尔夫球,我们可以省略 Exception 的实例化,而是将 Exception 类作为参数传递。这些更改将导致以下代码:
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
Run Code Online (Sandbox Code Playgroud)
当我们运行它时,我们将得到与以前相同的结果。它只是更短。
小智 16
实际上,有一种方法,但它是非常人为的.
您可以使用compile()内置函数创建代码对象.这允许您使用raise语句(或任何其他语句),但它提出了另一个挑战:执行代码对象.通常的方法是使用该exec语句,但这会导致您回到原始问题,即您无法在lambda(或者)中执行语句eval().
解决方案是一个黑客.像一个lambda语句的结果的Callables 都有一个属性__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()调用.这个map操作的结果是一个包含一个条目的列表,返回的lambda x, y, z是一个条目,这就是为什么我们需要这个lambda:如果我们__setattr__马上使用,我们就会丢失lambda: 0对象的引用!
最后,map()执行调用返回的列表的第一个(也是唯一的)元素,导致调用代码对象,最终引发所需的异常.
它工作(在Python 2.6中测试),但它绝对不漂亮.
最后一点说明:如果你有权访问该types模块(需要import在你之前使用该语句eval),那么你可以稍微缩短这段代码:使用types.FunctionType()你可以创建一个执行给定代码对象的函数,这样你就赢了不需要创建虚函数lambda: 0并替换其__code__属性的值.
Kyl*_*and 15
如果你想要的只是一个引发任意异常的lambda表达式,你可以使用非法表达式完成此操作.例如,lambda x: [][0]将尝试访问空列表中的第一个元素,这将引发IndexError.
请注意:这是一个黑客,而不是一个功能.不要使用这是另一个人可能看到或使用的任何(非代码 - 高尔夫)代码.
Atn*_*Atn 11
每次我想这样做时,都是在测试中我想断言未调用函数。
对于这个用例,我发现使用具有副作用的模拟会更清楚
from unittest.mock import Mock
MyClass.my_method = Mock(side_effect=AssertionError('we should not reach this method call')
Run Code Online (Sandbox Code Playgroud)
它也适用于其他设置,但我不想在我的主应用程序中依赖单元测试