就一般工作中,我们会考虑将not a in b改写成a not in b的写法,但似乎是为什么呢?类似的,还有not a is b与a is not b,如果就单单阅读角度考虑的话,确实a not in b与b is not none更加的贴近于英语的语法,阅读起来会更加顺畅(但也看人)。但这两种写法,本质上究竟有什么区别呢?下面以not a is none以及a is not none举例。
PyObject * PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, PyObject *lineno_obj) { for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) { reoptimize_current: opcode = codestr[i]; switch (opcode){ /* not a is b --> a is not b not a in b --> a not in b not a is not b --> a is b not a not in b --> a in b */ case COMPARE_OP: j = GETARG(codestr, i); if (j < 6 || j > 9 || codestr[i+3] != UNARY_NOT || !ISBASICBLOCK(blocks,i,4)) continue; SETARG(codestr, i, (j^1)); codestr[i+3] = NOP; break; } } }
1 0 LOAD_NAME 0 (a) 2 LOAD_CONST 0 (None) 4 COMPARE_OP 8 (is) 6 UNARY_NOT case COMPARE_OP: // 来到比较操作符,也即位置4 j = GETARG(codestr, i); // 获取对应的参数 这里拿到的就是8 if (j < 6 || j > 9 || // 比较下该操作是不是in, not in, is, is not,更详细的可以查下cmp_op表,或者看下官方的dis文档。 codestr[i+3] != UNARY_NOT || // 往后下一个操作符是不是 NOT !ISBASICBLOCK(blocks,i,4)) // 是不是特殊的基础代码块 continue; // 拿not xx is xx举例, // 那么此时j=8,要变成xx is not xx,那么只需要将j设置为9, // 并且下个操作符设置为空即可(这里生成的Nop后边会移除) SETARG(codestr, i, (j^1)); codestr[i+3] = NOP; break;
然后对每个操作符进行分支处理,对于操作符COMPARE_OP,通过if语句,来判断操作数位置,以及下个操作符是否是否为not(这里优化的就是not相关的)或者是基础代码块。如果识别出是可优化内容,即not a is b, not a in b, not a is not b, not a not in b,那么重新设置一下操作符信息,然后再把哪个not对应的操作符设置为空。相当于就变成了a is not b,a not in b,a is b,a in b。优化结束,最终字节码就变成了。
/* Replace UNARY_NOT POP_JUMP_IF_FALSE with POP_JUMP_IF_TRUE */ /* Skip over LOAD_CONST trueconst POP_JUMP_IF_FALSE xx. This improves "while 1" performance. */ /* Try to fold tuples of constants (includes a case for lists which are only used for "in" and "not in" tests). Skip over BUILD_SEQN 1 UNPACK_SEQN 1. Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2. Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */