Python3.5 和 Python3.6 的源代码有什么区别?

What's the difference here between the source codes of Python3.5 and Python3.6?

 testing on ../../test/test_patm.py
python: Python/compile.c:4420: int assemble_lnotab(struct assembler *, 
struct instr *): Assertion `d_lineno >= 0' failed.
Aborted

当 运行 我的测试程序出现上述错误时。 最后我发现 Python3.5 和 Python3.6 的源代码差别很小。 只有一行:

Python3.5

static int
assemble_lnotab(struct assembler *a, struct instr *i)
{
  int d_bytecode, d_lineno;
  Py_ssize_t len;
  unsigned char *lnotab;

  d_bytecode = a->a_offset - a->a_lineno_off;
  d_lineno = i->i_lineno - a->a_lineno;

  assert(d_bytecode >= 0);
  assert(d_lineno >= 0);   // the only difference

  if(d_bytecode == 0 && d_lineno == 0)
      return 1;
  ...

Python 3.6

static int
assemble_lnotab(struct assembler *a, struct instr *i)
{
    int d_bytecode, d_lineno;
    Py_ssize_t len;
    unsigned char *lnotab;

    d_bytecode = (a->a_offset - a->a_lineno_off) * sizeof(_Py_CODEUNIT);
    d_lineno = i->i_lineno - a->a_lineno;

    assert(d_bytecode >= 0);

    if(d_bytecode == 0 && d_lineno == 0)
        return 1;

如果我刚刚删除 assert(d_lineno >= 0); 会怎样?

您使用的是 调试 版本 3.5。在 Python 3.5 和 any 以前的版本中,单个字节码块(即模块或函数的字节码)中的行编号必须是 单调,即每个操作码必须映射到源代码中的一行,其行号必须大于或等于 前一个的行​​号 操作码。这曾经在调试版本中检查过;在 Python 的发布版本中,不会编译 assert,但生成的行号选项卡无论如何都是无效的。

Issue 26107 on bugs.python.org 中对此进行了讨论。行号的单调性要求被认为不利于优化,其中许多优化会重新组织生成的字节码。因此,在 3.6 中删除了检查以及其他更改,这些更改使行号增量成为有符号整数。

您可以非常安全地注释掉 this 断言,因为发布版本无论如何都会删除它,但不要指望调试在生成的文件中正常工作作为行号选项卡的代码现在无效。

作为替代方案,如果您正在重组 AST 中的行或类似的东西,您可以将 all 行号设置为 0 - 而不仅仅是缺失的行号;或者您可以生成不违反单调性规则的假行号。


生成的 AST 出现巧合问题,因为 ast.fix_missing_locations 会将 0 的行号写入任何缺少行号的节点。如果部分 AST 包含行号,因为它们源自 ast.parse,那么生成的 AST 树很可能会破坏单调性要求——这只会再次导致 [=52= 的非发布版本出现问题]s < 3.6.


另一个与这里的错误无关的变化是从字节码到 wordcode 的变化,它也在 Python 3.6 中引入。这里每个操作码都是一个 16 位字,而不是一个带有可能扩展参数的 8 位字节。这就是偏移量乘以 sizeof(_Py_CODEUNIT);.

的原因