当前位置:  开发笔记 > 编程语言 > 正文

使用Pythons的三元运算符与lambda结合使用的意外输出

如何解决《使用Pythons的三元运算符与lambda结合使用的意外输出》经验,为你挑选了1个好方法。

我有一个特定的情况,我想做以下(实际上它比这更多涉及,但我把问题简化为本质):

>>> (lambda e: 1)(0) if (lambda e: True)(0) else (lambda e: 2)(0)
True

这是一种难以写的方式:

>>> 1 if True else 2
1

但实际上'1','True'和'2'是需要评估的附加表达式,需要变量'e',我为此简化代码示例设置为'0'.

请注意上面两个表达式的输出差异

>>> (lambda e: 1)(0)
1
>>> (lambda e: True)(0)
True
>>> (lambda e: 2)(0)
2

有趣的是,这是一个特殊情况,因为如果我将'1'替换为'3',我会获得预期/期望的结果:

>>> (lambda e: 3)(0) if (lambda e: True)(0) else (lambda e: 2)(0)
3

如果我将'1'替换为'0'(这也可能是特殊情况,因为1 == True和0 == False),这甚至是正确的

>>> (lambda e: 0)(0) if (lambda e: True)(0) else (lambda e: 2)(0)
0

另外,如果我将'True'替换为'not False'或'not not True',它仍然有效:

>>> (lambda e: 1)(0) if (lambda e: not False)(0) else (lambda e: 2)(0)
1
>>> (lambda e: 1)(0) if (lambda e: not not True)(0) else (lambda e: 2)(0)
1

另一个替代公式使用通常的if..then..else语句,并且不会产生错误:

>>> if (lambda e: True)(0):
    (lambda e: 1)(0)
else:
    (lambda e: 2)(0)

1

什么解释了这种行为?如何以一种很好的方式解决这种行为(避免使用'not not True'或其他东西?

谢谢!



1> user2357112 ..:

我想我弄清楚了为什么会发生错误,以及为什么你的repro是Python 3特有的.

代码对象按值而不是通过指针进行相等比较,奇怪的是:

static PyObject *
code_richcompare(PyObject *self, PyObject *other, int op)
{
    ...

    co = (PyCodeObject *)self;
    cp = (PyCodeObject *)other;

    eq = PyObject_RichCompareBool(co->co_name, cp->co_name, Py_EQ);
    if (eq <= 0) goto unequal;
    eq = co->co_argcount == cp->co_argcount;
    if (!eq) goto unequal;
    eq = co->co_kwonlyargcount == cp->co_kwonlyargcount;
    if (!eq) goto unequal;
    eq = co->co_nlocals == cp->co_nlocals;
    if (!eq) goto unequal;
    eq = co->co_flags == cp->co_flags;
    if (!eq) goto unequal;
    eq = co->co_firstlineno == cp->co_firstlineno;
    if (!eq) goto unequal;

    ...

在Python 2中,lambda e: True进行全局名称查找并lambda e: 1加载常量1,因此这些函数的代码对象不能相等.在Python 3中,True是一个关键字和两个lambdas加载常量.因为1 == True,代码对象足够相似,所有检查都code_richcompare通过,并且代码对象比较相同.(其中一项检查是针对行号,因此仅当lambda在同一行时才会出现错误.)

字节码编译器调用ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts)创建将LOAD_CONSTlambda代码加载到堆栈上的指令,并ADDOP_O使用dict来跟踪它添加的对象,以试图在复制常量之类的东西上节省空间.它有一些处理,以区分之类的东西0.0,0-0.0原本比较平等的,但它是没有预料到他们会永远需要处理等,但是,不等价代码对象.代码对象没有被正确区分,并且两个lambda最终共享一个代码对象.

通过更换True1.0,我们可以重现上的Python 2的错误:

>>> f1, f2 = lambda: 1, lambda: 1.0
>>> f2()
1

我没有Python 3.5,所以我无法检查该版本中是否仍然存在该bug.我没有在bug跟踪器中看到有关该bug的任何内容,但我本可以错过报告.如果错误仍然存​​在且尚未报告,则应报告.

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有