Python:为什么应该禁止 'from <module> import *'?
如果你在程序(或模块)的中间有
from <module> import *
那么你会收到这样的警告:
/tmp/foo:100: SyntaxWarning: import * only allowed at module level
我明白为什么通常不推荐使用 import *
(因为命名空间不清晰),
但在很多情况下,这样做会很方便,特别是当代码不和其他人共享的时候。
那么,有人能详细解释一下为什么 from <module> import *
在所有情况下都应该被禁止吗?
6 个回答
在任何语言层面上,from amodule import *
这个设计决定看似当时是个好主意,但实际上却带来了很多麻烦,可能唯一的例外就是在交互式解释器中探索时(即使如此,我也不太喜欢这个方法——import module as m
只需要多打两个字符就能使用带前缀的名称,而带前缀的名称总是比简单名称更清晰、更灵活,更不用说在交互式探索中,使用 m
来调用 help(m)
、reload(m)
等功能是多么方便!)。
这种混乱的写法让阅读代码的人(通常是在试图帮助调试时)很难理解那些神秘出现的名称是从哪里来的——如果在同一层面上使用多次,那就几乎不可能了;即使只用一次,每次在确认这个混乱的简单名称确实来自于某个模块之前,也得费劲地重新阅读整个模块。
而且,模块的作者通常不会费尽心思去“支持”这种糟糕的写法。如果在你的代码中有使用 sys.argv
(当然,模块最上面得有 import sys
),你怎么能知道 sys
是应该是那个模块呢……还是说是某个完全不同的模块(或者根本不是模块)来自于 ... import *
呢?!把这个问题扩展到你使用的所有带前缀的名称上,结果就是痛苦——还有那些需要长时间调试的神秘错误(通常还得借助那些懂 Python 的人来帮忙……!)。
在一个函数内部,添加和覆盖任意本地名称的方式会更糟。作为一个基本但重要的优化,Python 编译器会在函数体内查找每个简单名称的赋值或其他绑定语句,并将看到的那些名称视为“本地”的(其他的必须是全局的或内置的)。如果使用了 import *
(就像没有明确字典作为命名空间的 exec somestring
一样),突然之间,哪些名称是本地的,哪些是全局的就变得完全不清楚了——所以可怜的编译器不得不对每个名称查找使用最慢的策略,使用字典来处理本地变量(而不是它通常使用的紧凑“向量”),并且每次引用简单名称时都要进行多达三次字典查找,反复进行。
去任何 Python 交互式提示符那里。输入 import this
。你会看到什么?Python 的哲学。那段文字中最后一句,可能也是最重要的智慧是什么呢……?
命名空间是个非常棒的主意——让我们多用这些!
通过强迫使用简单名称,而不是使用更好的带前缀名称,你实际上是在做这个明智建议的完全相反的事情:你不是在欣赏命名空间的伟大和重要性,反而是在破坏两个完全好的、可以直接使用的命名空间(一个是你正在导入的模块的命名空间,另一个是你导入它的词法作用域的命名空间),把它们搞成一个混乱、bug多、慢、死板、无法使用的糟糕局面。
如果我能回去改变 Python 的一个早期设计决定(这很难选择,因为 def
和特别是 lambda
的用法,和 JavaScript 更易读的 function
叫法是个接近的第二选择;-),我会让 import *
的想法从 Guido 的脑海中抹去。没有任何在交互式提示符下所谓的便利能抵消它带来的麻烦……!
关于Python 2.1的发布说明,里面提到了一些原因,解释了为什么会有这样的限制:
这个变化的一个副作用是,在某些情况下,
from module import *
和exec
这两个语句在函数内部是被禁止的。Python的参考手册一直说,from module import *
只能在模块的最顶层使用,但之前的CPython解释器并没有严格执行这个规则。为了实现嵌套作用域,负责把Python代码转换成字节码的编译器需要生成不同的代码来访问外部作用域的变量。而from module import *
和exec
会让编译器无法搞清楚这些变量,因为它们会在本地命名空间中添加一些在编译时无法知道的名字。因此,如果一个函数里面有函数定义或带有自由变量的lambda表达式,编译器就会通过抛出一个SyntaxError(语法错误)来提醒你。
我觉得你说的“在程序中间”是指在一个函数定义里面使用 import:
def f():
from module import * # not allowed
这样做是不允许的,因为这会让优化函数的内容变得很困难。Python 在编译函数的时候,需要知道所有函数内部变量的名字,这样才能把变量的引用优化成对虚拟机操作栈的操作,或者至少是对局部变量的操作,而不是在外部命名空间中查找。如果你能把一个模块的所有内容都放到函数的局部命名空间里,那么编译器就得假设函数里的任何名字都有可能指向模块的全局变量,因为通过 from module import *
导入的名字只有在运行时才能知道。
把 from module import *
放在顶层声明之间虽然风格不太好,但其实是允许的:
def f():
...
from module import *
def g():
...
编辑于2013年4月: 在研究其他内容时,我发现这个限制是在 Python 2.1 中引入的,原因是 “嵌套作用域”特性 (PEP 227)。引用链接中的内容:
这个变化的一个副作用是,在某些条件下,
from module import *
和exec
语句在函数作用域内变得不合法。Python 的参考手册一直说from module import *
只能在模块的顶层使用,但 CPython 解释器以前并没有强制执行这个规则。作为嵌套作用域实现的一部分,将 Python 源代码转换为字节码的编译器必须生成不同的代码来访问包含作用域中的变量。from module import *
和exec
让编译器无法判断,因为它们在编译时无法知道会添加哪些名字到局部命名空间。因此,如果一个函数包含函数定义或带有自由变量的lambda
表达式,编译器会通过抛出SyntaxError
异常来标记这个问题。
这澄清了评论中讨论的 Python 3.x 和 2.x 的行为。虽然这总是与语言规范相悖,但 CPython 2.1 到 2.7 只会在 from module import *
可能影响编译器判断变量是局部的还是在包含作用域中的情况下发出错误。在 3.x 中,这已经被提升为无条件错误。
编辑补充:... 显然 flashk 多年前在另一个回答中指出了这一点,引用了“Python 2.1 新特性”中的同一段话。大家现在去给那个回答点赞吧。