blog icon indicating copy to clipboard operation
blog copied to clipboard

Python中的条件语句

Open junnplus opened this issue 7 years ago • 0 comments

if 1 > 2:
    pass
elif 1 < 3:
    pass
else:
    pass

对应的字节码:

  1           0 LOAD_CONST               0 (1)
              2 LOAD_CONST               1 (2)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       10

  2           8 JUMP_FORWARD            10 (to 20)

  3     >>   10 LOAD_CONST               0 (1)
             12 LOAD_CONST               2 (3)
             14 COMPARE_OP               0 (<)
             16 POP_JUMP_IF_FALSE       20

  4          18 JUMP_FORWARD             0 (to 20)

  6     >>   20 LOAD_CONST               3 (None)
             22 RETURN_VALUE

上一篇提到了COMPARE_OP指令最后会预判下一条指令,这里就会跳跃到PREDICTED(POP_JUMP_IF_FALSE)

#define PREDICTED(op)           PRED_##op:

PREDICTED(POP_JUMP_IF_FALSE);
TARGET(POP_JUMP_IF_FALSE) {
    PyObject *cond = POP();
    int err;
    if (cond == Py_True) {
        Py_DECREF(cond);
        FAST_DISPATCH();
    }
    if (cond == Py_False) {
        Py_DECREF(cond);
        JUMPTO(oparg);
        FAST_DISPATCH();
    }
    err = PyObject_IsTrue(cond);
    Py_DECREF(cond);
    if (err > 0)
        ;
    else if (err == 0)
        JUMPTO(oparg);
    else
        goto error;
    DISPATCH();
}

POP_JUMP_IF_FALSE指令POP栈顶对象,并判断其bool值。

如果是False的话就JUMPTO跳跃到指定位置的字节码指令上。因为按照正常流程,1 > 2判断为False会进行下一个elif的判断,而这个elif判断的字节码指令的位置就是跳跃的位置。

如果判断为True就进入这个判断对应的逻辑处理,这边只有pass语句,所以没有任何字节码的产生, 下一条指令JUMP_FORWARD是结束这个判断逻辑处理:

TARGET(JUMP_FORWARD) {
    JUMPBY(oparg);
    FAST_DISPATCH();
}

#define JUMPBY(x)       (next_instr += (x) / sizeof(_Py_CODEUNIT))

JUMP_FORWARD指令会跳跃到偏移下一条指令oparg偏移量的位置,其实就是整个if语句的末尾,这个偏移量和前面的跳跃位置都是在编译阶段就计算出来的。

至于POP_JUMP_IF_TRUE指令是在诸如if not 1 > 2这样的语句下产生的。POP_JUMP_IF_TRUE指令正好是和POP_JUMP_IF_FALSE做相反的判断跳跃。

POP_JUMP_IF_TRUE这样的指令会比产生UNARY_NOT指令+POP_JUMP_IF_FALSE指令在执行上更快。

junnplus avatar Apr 05 '18 17:04 junnplus