抽象语法树中 Python 个 f 字符串的行号

Line numbers of Python f-strings in abstract syntax tree

当我使用 ast 模块解析源代码时,为什么 Name 内部 FormattedValuelineno 属性设置为 1,即使 f -字符串不在第 1 行?

我试图查看函数定义中包含了哪些源代码行,因此我遍历了 FunctionDef 节点下方的抽象语法树节点。我将所有 lineno 属性收集到一个集合中,这告诉我哪些行是函数定义的一部分。

然而,当f-strings出现在Python 3.6时,他们以某种方式破坏了这个技术。这是问题的示例:

import ast

code = """\

f'x{y}'
"""

tree = ast.parse(code)

print(ast.dump(tree, include_attributes=True))

这是该脚本的输出:

Module(body=[Expr(value=JoinedStr(values=[Str(s='x', lineno=2, col_offset=0), FormattedValue(value=Name(id='y', ctx=Load(), lineno=1, col_offset=1), conversion=-1, format_spec=None, lineno=2, col_offset=0)], lineno=2, col_offset=0), lineno=2, col_offset=0)])

我知道这很难读,所以这里是带有一些额外空格的相同输出:

Module(body=[Expr(value=JoinedStr(values=[
    Str(s='x', lineno=2, col_offset=0), 
    FormattedValue(value=Name(id='y',
                              ctx=Load(),
                              lineno=1,
                              col_offset=1),
                   conversion=-1,
                   format_spec=None,
                   lineno=2,
                   col_offset=0)], lineno=2, col_offset=0), lineno=2, col_offset=0)])

唯一的源代码在第 2 行,那么这部分输出是怎么回事?

Name(id='y',
     ctx=Load(),
     lineno=1,
     col_offset=1)

我以为可能是字符串中的行号,但是当我尝试这个时,它仍然报告lineno=1

code = """\

f'''

x{y}'''
"""

经过一些研究,似乎 f-strings 比我想象的更强大。每组大括号不仅仅是 Python 以前版本中的一个字典键,它是一个完整的 Python 代码块。这意味着您可以像这样在大括号中编写 Python 表达式:

f'x{y + z}'

根据这种理解,lineno 值是大括号 内的行号 ,而不是字符串或整个文件内的行号。为了验证该理论,我尝试了这段代码,lineno 更改为 3.

code = """\

f'''x{

y}'''
"""

如果我只是忽略来自 FormattedValue 个节点的行号,我的代码将再次运行。