为什么Python优化"如果0"但不是"如果没有"?

时间:2017-06-06 12:19:32

标签: python python-2.7 cpython

为什么要编译像

这样的条件表达式
Button {
    width: 100
    text:  'Very very long button description.'
    Component.onCompleted: contentItem.wrapMode = Text.WordWrap
}

使用数字的分支会被优化,但那些使用def f(): if None: print(222) if 0: print(333) 的分支会被淘汰?例如:

None

3 0 LOAD_CONST 0 (None) 3 POP_JUMP_IF_FALSE 14 4 6 LOAD_CONST 1 (222) 9 PRINT_ITEM 10 PRINT_NEWLINE 11 JUMP_FORWARD 0 (to 14) 5 >> 14 LOAD_CONST 0 (None) 17 RETURN_VALUE if 0在哪些情况下表现不同?

2 个答案:

答案 0 :(得分:8)

我的猜测:这是一个疏忽,因为None只是python-2.x中一个特殊的名称(或全局)。

如果您查看bytecode-optimizer code in python-2.x

switch (opcode) {

   /* ... More cases ... */

        /* Replace LOAD_GLOBAL/LOAD_NAME None
           with LOAD_CONST None */
    case LOAD_NAME:
    case LOAD_GLOBAL:
        j = GETARG(codestr, i);
        name = PyString_AsString(PyTuple_GET_ITEM(names, j));
        if (name == NULL  ||  strcmp(name, "None") != 0)
            continue;
        for (j=0 ; j < PyList_GET_SIZE(consts) ; j++) {
            if (PyList_GET_ITEM(consts, j) == Py_None)
                break;
        }
        if (j == PyList_GET_SIZE(consts)) {
            if (PyList_Append(consts, Py_None) == -1)
                goto exitError;
        }
        assert(PyList_GET_ITEM(consts, j) == Py_None);
        codestr[i] = LOAD_CONST;
        SETARG(codestr, i, j);
        cumlc = lastlc + 1;
        break;      /* Here it breaks, so it can't fall through into the next case */

        /* Skip over LOAD_CONST trueconst
           POP_JUMP_IF_FALSE xx. This improves
           "while 1" performance. */
    case LOAD_CONST:
        cumlc = lastlc + 1;
        j = GETARG(codestr, i);
        if (codestr[i+3] != POP_JUMP_IF_FALSE  ||
            !ISBASICBLOCK(blocks,i,6)  ||
            !PyObject_IsTrue(PyList_GET_ITEM(consts, j)))
            continue;
        memset(codestr+i, NOP, 6);
        cumlc = 0;
        break;

   /* ... More cases ... */

}

您可能会注意到None已加载LOAD_GLOBALLOAD_NAME,然后被LOAD_CONST替换。

但是:在将break替换为LOAD_CONST之后,如果常量为'n',则不能进入NOP情况,在该情况下,块将被替换为True t None

在python-3.x中,优化器不需要特殊情况下的名称(或全局)LOAD_CONST,因为它总是加载switch (opcode) { /* ... More cases ... */ /* Skip over LOAD_CONST trueconst POP_JUMP_IF_FALSE xx. This improves "while 1" performance. */ case LOAD_CONST: CONST_STACK_PUSH_OP(i); if (nextop != POP_JUMP_IF_FALSE || !ISBASICBLOCK(blocks, op_start, i + 1) || !PyObject_IsTrue(PyList_GET_ITEM(consts, get_arg(codestr, i)))) break; fill_nops(codestr, op_start, nexti + 1); CONST_STACK_POP(1); break; /* ... More cases ... */ } bytecode-optimizer reads

LOAD_NAME

LOAD_GLOBALif None不再有特殊情况,因此if False(但False - LOAD_CONST也在python-3中成为常量。 x)将进入NOP案例,然后由# server.R function(input, output) { output$make_box <- renderUI({ checkboxInput("checkbox", label = "Choice A", value = setboxon()) }) setboxon <- eventReactive(input$action, { if(is.null(input$checkbox)) return(FALSE) else if(input$checkbox==TRUE) return(FALSE) else return(TRUE) }) # check status off checkbox value output$value <- renderPrint({ input$checkbox }) } 替换。

答案 1 :(得分:1)

  

免责声明:这不是一个真正的答案,而只是我的成功报告   尝试覆盖CPython 2.7中的None尽管受到了保护   编译器。

我在CPython 2.7中找到了一种覆盖None的方法,虽然它涉及一个肮脏的技巧,并且可以类似地对文字进行。即,我替换代码对象的co_consts字段中的常量条目#0:

def makeNoneTrueIn(func):
    c = func.__code__
    func.__code__ = type(c)(c.co_argcount,
                            c.co_nlocals,
                            c.co_stacksize,
                            c.co_flags,
                            c.co_code,
                            (True, ) + c.co_consts[1:],
                            c.co_names,
                            c.co_varnames,
                            c.co_filename,
                            c.co_name,
                            c.co_firstlineno,
                            c.co_lnotab,
                            c.co_freevars,
                            c.co_cellvars)


def foo():
    if None:
        print "None is true"
    else:
        print "None is false"

foo()
makeNoneTrueIn(foo)
foo()

输出:

None is false
None is true