我可以使用以下语法创建多行字符串:
string = str("Some chars "
"Some more chars")
这将产生以下字符串:
"Some chars Some more chars"
Python 是连接这两个单独的字符串还是编辑器/编译器将它们视为单个字符串?
P.s:我只是想了解内部原理。我知道还有其他方法来声明或创建多行字符串。
附注二:
这也是有效的:
string = str('''Some chars '''
"Some more chars"'-otherchar')
输出:
Some chars Some more chars-otherchar
阅读参考手册,就在那里。 具体来说:
允许使用多个相邻的字符串或字节文字(由空格分隔),可能使用不同的引用约定,并且它们的含义与其串联相同。因此,“hello”“world”相当于“helloworld”。 此功能可用于减少所需的反斜杠数量,方便地跨长行分割长字符串,甚至可以为部分字符串添加注释,
(强调我的)
这就是原因:
string = str("Some chars "
"Some more chars")
与:
str("Some chars Some more chars")
完全相同。
只要出现字符串文字、列表初始化、函数调用(如上面
str
的情况)等,都会执行此操作。
唯一需要注意的是,字符串文字不包含在分组分隔符、
()
、{}
或[]
之一之间,而是分布在两个单独的物理线之间。在这种情况下,我们可以选择使用反斜杠字符来连接这些行并获得相同的结果:
string = "Some chars " \
"Some more chars"
当然,在同一物理行上串联字符串不需要反斜杠。 (
string = "Hello " "World"
就可以了)
Python 是连接这两个单独的字符串还是编辑器/编译器将它们视为单个字符串?
Python 是,现在何时 Python 确实做到了这一点,这就是事情变得有趣的地方。
据我所知(对此持保留态度,我不是解析专家),当Python将给定表达式的解析树(
LL(1)
解析器)转换为它时,就会发生这种情况对应的 AST (抽象语法树)。
parser
模块获取解析树的视图:
import parser
expr = """
str("Hello "
"World")
"""
pexpr = parser.expr(expr)
parser.st2list(pexpr)
这会转储一个相当大且令人困惑的列表,该列表表示从
expr
中的表达式解析的具体语法树:
-- rest snipped for brevity --
[322,
[323,
[3, '"hello"'],
[3, '"world"']]]]]]]]]]]]]]]]]],
-- rest snipped for brevity --
数字对应于解析树中的符号或标记,从符号到语法规则和标记到常量的映射分别在
Lib/symbol.py
和 Lib/token.py
中。
正如您在我添加的片段版本中看到的,您有两个不同的条目对应于解析的表达式中的两个不同的
str
文字。
ast
模块查看上一个表达式生成的 AST 树的输出:
p = ast.parse(expr)
ast.dump(p)
# this prints out the following:
"Module(body = [Expr(value = Call(func = Name(id = 'str', ctx = Load()), args = [Str(s = 'hello world')], keywords = []))])"
在这种情况下,输出更加用户友好;您可以看到函数调用的
args
是单个连接字符串 Hello World
。
此外,我还偶然发现了一个很酷的 module,它可以为
ast
节点生成树的可视化。使用它,表达式 expr
的输出可视化如下:
图像被裁剪以仅显示表达式的相关部分。
如您所见,在终端叶节点中,我们有一个
str
对象,即 "Hello "
和 "World"
的连接字符串,即 "Hello World"
。
如果你足够勇敢,请深入研究源代码,将表达式转换为解析树的源代码位于
Parser/pgen.c
,而将解析树转换为抽象语法树的代码位于 Python/ast.c
.
此信息适用于
Python 3.5
,我很确定除非您使用某些非常旧的版本(< 2.5
),否则功能和位置应该是相似的。
此外,如果您对 python 接下来的整个编译步骤感兴趣,核心贡献者之一 Brett Cannon 在视频从源代码到代码:CPython 编译器如何工作中提供了很好的温和介绍。