Python中`from ... import ...`语法背后的原理

17

我一直想知道为什么从一个模块导入特定对象的语法是 from module import x, y, z,而不是 import x, y, z from module。我不是母语为英语的人,但后者难道不更正确/自然吗?

那么,将 from 放在前面的原因是什么呢?仅仅是为了简化语法(需要更少的先行符)吗?还是尝试使这两种导入方式在视觉上更加不同?或者这是其中一种情况,即显而易见的方式“除非你是荷兰人,否则一开始并不明显”? ;)


1
我仍在学习Python,但依我之见,以下代码似乎更符合语言的一致性:import(module, x, y, z)。根本不需要使用from - FMc
5
如果您不希望 import 获得特殊的语法处理,那么您需要编写 import('module', 'x', 'y', 'z'),传递对象的 名称,因为在 导入 之后对象本身是不可用的。 - dan04
2
我见过的最好的导入语法是 Haskell 中的 import module (a, b, c)。它干净、清晰,使多个导入语句看起来统一和对齐(除了 import qualified 的情况,不幸的是)。而且我不知道为什么 GvR 选择了那个丑陋的 from ... import - firegurafiku
1
以介词开头的句子既不是语法错误,也不是那么罕见。例如,“从梅德福德出发,沿着5号州际公路向南行驶。”这完全取决于哪种方式更容易理解句子的结构。对于许多情况,包括这个例子,先提供背景信息再进行动作更加合理。 - John F. Miller
8个回答

21

我不知道为什么要这样做,但这是我会采用的方式,因为作为一个工程师,从一般范畴开始并逐渐深入具体细节对我来说更加自然。

如果按顺序处理,这还意味着解析器需要存储较少的内容。例如:

import x, y, z from a

你需要记住 xyz。具体如下:

from a import x, y, z

你只需要记住a


这就是我第一次遇到Perl的后置if变体时为什么会有很多麻烦:

$x = $y if $y > 40;

由于您不知道所读内容是否有条件限制 事先


1
既然你提到“后缀if”,我认为这会增加可读性... +1,除非A.M.的超级答案出现;) - user395760
所以,将近十年后,我只想补充一下,现在我们有了更好的Python IDE,语法from foo import ba<ctrl+space>允许IDE实现自动完成(提供选项barbatbaz...),因为它知道你要导入什么,然后再尝试自动完成。 - michael

16

纯属猜测,可能完全没有意义,但我知道来自 Modula-2 的语法(天啊,那是二十年前的事了,我感觉自己老了)……也许 Python 受到了它的启发?


5
最简单且最有可能正确的答案是,GvR 在设计 Python 时从各种语言中借鉴了构造方式,包括 Modula-2,这可能直接来源于该语言。 - PaulMcG

5

除了直接询问Guido,我认为你不会找到任何关于这个的解释。

这种语法从一开始就存在。我能找到的最早版本的Python源代码是Python 1.0.1。查看语法文件中的更改日志,我们可以找到更早版本的参考资料。在Python的第2版(我想我们正在讨论0.9.0之后的第二个发布版本)中,我们有以下注释:

#   added 'from' NAME option on import clause, and '*' to import all;

这是与之同时添加的

#   added class definition.

因此,随着类的引入,import语句也同时应运而生。这是Python还是Guido van Rossum独自开发项目时的情况。换句话说,你要寻找的答案已经随时间的流逝而消逝了。
现在,我来推测一下为什么import语句使用from x import y而不是import y from ximport语句文档提供了实现import的基本算法:

导入语句分为两步执行:(1) 查找并初始化模块(如有必要); (2) 在局部名称空间中(该导入语句所在作用域)定义一个或多个名称。导入语句有两种形式,在是否使用from关键字上有所不同。第一种形式(不使用from)对列表中的每个标识符重复执行这些步骤。使用from的形式执行步骤(1) 一次,并重复执行步骤(2)。

在这两种版本的import语句中,该算法的第一步总是在最左边。我认为这对于一名语言实现者来说是最明显的排序方式,即使英语在反过来读可能更自然。

5

我不知道这种语法的完整历史,因为它可以追溯到 Python 1.x 时代。但是我发现能够扫描源代码的左侧,并快速找到脚本依赖的模块名称非常有用。如果一个语句是 "import a,b,c,d,e,really_long_name, alsdf,lsdf from blah",那么我需要花一些时间才能找到该脚本依赖于 blah。


1
实际上,这并不奇怪。看看我们在其他语言中如何“导入”、“包含”或“需要”。我们总是先指定命名空间。例如,在 PHP 中包含“inc/config.php”。因此,在某种程度上,它保持了我们通常包含文件或模块的方式。

1

在英文中,可能更合理地说 from module import x, y, z,但在编程中,先引入更通用的项目,然后再引入更具体的细节是更有意义的。 这可能不是唯一的原因,但对编译器或解释器来说会更容易处理。 尝试编写编译器,你就会明白我的意思 :D


我完全理解编译器编写者的差异,但是作为一个程序员,如果我在意这个的话,我会坚持使用带有助记符的汇编语言 :D - user395760
3
这段话的意思是:这不仅有助于编译器编写者,还有助于集成开发环境给你提供选项,如果你首先编写了“import”,那么IDE无法为你提供任何选项,因为它不知道你想从哪个模块导入。 - Scarlet
1
好观点。这让我想起了SQL语句SELECT Columns FROM Table,它在自动完成方面也存在同样的问题。 - dan04

0

这取决于你使用的编程语言语法。对我来说,使用这样的导入方式更容易阅读。

使用这种方式更容易阅读和理解吗?

From Grocery buy apple and orange

或者

Buy apple and orange from grocery.
Buy apple and orange from supermarket

第一个更适合我...


0

来自https://docs.python.org/3/reference/simple_stmts.html#import:

import foo # 导入foo并在本地绑定 import foo.bar.baz # 导入foo.bar.baz,foo在本地绑定 import foo.bar.baz as fbb # 导入foo.bar.baz并绑定为fbb from foo.bar import baz # 导入foo.bar.baz并绑定为baz from foo import attr # 导入foo并将foo.attr绑定为attr

我得出结论这是一个本地可用性/绑定的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接