为什么 1 + 1 不使用 BINARY_ADD?

Why doesn't 1 + 1 use BINARY_ADD?

我这样做:

>>> dis.dis(lambda: 1 + 1)
0 LOAD_CONST        2 (2)
3 RETURN_VALUE

我期待 BINARY_ADD 操作码来执行加法。总和是如何计算的?

Python解释器从内向外解释,即它读取1 + 1将其计算为2然后创建returns 常量 2 的函数对象(注意这里的顺序!)。最后,dis 函数求值。新创建的 lambda 函数对象,它只是 returns 一个 2.

因此,在创建 lambda 函数对象时已经计算了 1+1,并且 dis.dis() 函数对解释器读取 1+1 时发生的加法一无所知并将其评估为 2

如果你这样做:

>>> dis.dis(lambda: x + 1)
  1           0 LOAD_GLOBAL              0 (x)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD          
              7 RETURN_VALUE  

您会注意到使用了 BINARY_ADD 指令,因为 x + 1 本身无法进一步简化。

这是 Python 的窥视孔优化器的工作。它在编译期间评估仅使用常量的简单操作,并将结果作为常量存储在生成的字节码中。

引用自Python 2.7.9 Source code,

            /* Fold binary ops on constants.
               LOAD_CONST c1 LOAD_CONST c2 BINOP -->  LOAD_CONST binop(c1,c2) */
        case BINARY_POWER:
        case BINARY_MULTIPLY:
        case BINARY_TRUE_DIVIDE:
        case BINARY_FLOOR_DIVIDE:
        case BINARY_MODULO:
        case BINARY_ADD:
        case BINARY_SUBTRACT:
        case BINARY_SUBSCR:
        case BINARY_LSHIFT:
        case BINARY_RSHIFT:
        case BINARY_AND:
        case BINARY_XOR:
        case BINARY_OR:
            if (lastlc >= 2 &&
                ISBASICBLOCK(blocks, i-6, 7) &&
                fold_binops_on_constants(&codestr[i-6], consts)) {
                i -= 2;
                assert(codestr[i] == LOAD_CONST);
                cumlc = 1;
            }
            break;

基本上,它会寻找这样的指令

LOAD_CONST c1
LOAD_CONST c2
BINARY_OPERATION

并对其进行评估,并用结果和 LOAD_CONST 指令替换这些指令。引用 comment in the fold_binops_on_constants function,

/* Replace LOAD_CONST c1. LOAD_CONST c2 BINOP
   with    LOAD_CONST binop(c1,c2)
   The consts table must still be in list form so that the
   new constant can be appended.
   Called with codestr pointing to the first LOAD_CONST.
   Abandons the transformation if the folding fails (i.e.  1+'a').
   If the new constant is a sequence, only folds when the size
   is below a threshold value.  That keeps pyc files from
   becoming large in the presence of code like:  (None,)*1000.
*/

此特定代码的实际计算发生 in this block

    case BINARY_ADD:
        newconst = PyNumber_Add(v, w);
        break;