usual me
usual me

Reputation: 8778

Why doesn't 1 + 1 use BINARY_ADD?

I do this:

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

I was expecting a BINARY_ADD opcode to perform the addition. How was the sum computed?

Upvotes: 6

Views: 205

Answers (2)

thefourtheye
thefourtheye

Reputation: 239473

This is the work of Python's peephole optimizer. It evaluates simple operations with only constants during the compile time itself and stores the result as a constant in the generated bytecode.

Quoting from the 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;

Basically, it looks for instructions like this

LOAD_CONST c1
LOAD_CONST c2
BINARY_OPERATION

and evaluates that and replaces those instructions with the result and a LOAD_CONST instruction. Quoting the 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.
*/

The actual evaluation of this particular code happens in this block,

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

Upvotes: 10

therealrootuser
therealrootuser

Reputation: 10914

The Python interpreter interprets from the inside out, that is, it reads the 1 + 1 evaluates it to 2, then creates a function object that returns the constant 2 (notice the order here!). Finally, the dis function evaluates. the newly created lambda function object, which simply returns a 2.

Thus, the 1+1 has already been computed when the lambda function object is created, and the dis.dis() function knows nothing about the addition that took place when the interpreter read 1+1 and evaluated it to 2.

If you do something like:

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

You'll notice that a BINARY_ADD instruction is used, since x + 1 can't be further simplified by itself.

Upvotes: 1

Related Questions