为什么“导入*”不好?
build议不要在Python中使用import *
。
任何人都可以请分享这个原因,这样我可以避免下一次做?
-
因为它将很多东西放到你的名字空间中(可能会隐藏以前导入的其他对象,而你不知道它)。
-
因为你不知道什么是导入,不能从哪个模块find某个东西容易导入(可读性)的地方。
-
因为你不能使用像
pyflakes
这样的酷工具来检测代码中的静态错误。
根据Python禅 :
显式比隐式更好。
…不能与此争论,当然?
你不通过**locals()
的function,是吗?
由于Python缺less一个“include”语句, 并且 self
参数是显式的, 并且范围规则非常简单,所以通常很容易将一个手指指向一个variables并告诉该对象来自何处 – 而不需要读取其他模块IDE的种类(无论如何,这是内省的方式,语言是非常dynamic的)。
import *
打破了这一切。
另外,它具有隐藏bug的具体可能性。
import os, sys, foo, sqlalchemy, mystuff from bar import *
现在,如果bar模块具有任何“ os
”,“ mystuff
”等属性,它们将覆盖显式导入的属性,并可能指向非常不同的东西。 在bar中定义__all__
通常是明智的 – 这表明隐式地导入了什么 – 但是仍然很难追踪对象来自哪里,而不需要读取和parsingbar模块并在其后导入。 import *
networkingimport *
是我获得项目所有权的第一件事。
不要误解我:如果import *
失踪了,我会哭的。 但必须谨慎使用。 一个很好的用例是通过另外一个模块提供一个Facade接口。 同样,使用条件导入语句,或者在函数/类名称空间内导入,需要一点训练。
我认为,在大中型项目中,或者与几个贡献者一起的小项目,在静态分析方面至less需要卫生 – 至less运行pyflakes,或者更好地configuration一个适当的pylint – 以便捕捉几种types的bug他们发生。
当然,因为这是python – 随意破坏规则,并探索 – 但要小心可能会增长十倍的项目,如果源代码缺less纪律,这将是一个问题。
那是因为你在污染命名空间。 你将在你自己的命名空间中导入所有的函数和类,这可能与你自己定义的函数冲突。
此外,我认为使用合格的名称对于维护任务更为明确; 你可以在代码行看到函数来自哪里,所以你可以更容易地查看文档。
在模块foo中:
def myFunc(): print 1
在你的代码中:
from foo import * def doThis(): myFunc() # Which myFunc is called? def myFunc(): print 2
在交互式会话中执行from ... import *
可以。
http://docs.python.org/tutorial/modules.html
请注意,一般来说,从模块或包导入
*
的做法是不被接受的,因为它经常导致代码的可读性差 。
假设你在一个叫做foo的模块中有下面的代码:
import ElementTree as etree
然后在你自己的模块中有:
from lxml import etree from foo import *
你现在有一个难以debugging的模块, 看起来好像有lxml的etree,但是真的有ElementTree。
这些都是很好的答案。 我将补充一点,当教新人在Python中编写代码时,处理import *
是非常困难的。 即使你或他们没有写代码,它仍然是一个绊脚石。
我教孩子们(大约8岁)用Python编程来操纵Minecraft。 我喜欢给他们一个有用的编码环境( Atom Editor ),并教导REPL驱动的开发(通过bpython )。 在Atom中,我发现提示/完成和bpython一样有效。 幸运的是,与其他一些统计分析工具不同,Atom不会被import *
迷惑。
然而,让我们from local_module import *
这个例子…在这个包装器中,他们from local_module import *
包含这个块列表的一堆模块。 让我们忽略命名空间冲突的风险。 通过from mcpi.block import *
做他们做这个模糊的types块的整个列表,你必须去看看什么是可用的东西。 如果他们使用from mcpi import block
,则可以键入walls = block.
然后会popup一个自动完成列表。
明白了人们放在这里的有效点。 但是我有一个论点,有时候,“明星import”可能并不总是一个坏习惯:
- 当我想以这样的方式构build我的代码,所有的常量到一个名为
const.py
的模块:- 如果我
import const
,那么对于每个常量,我必须把它称为const.SOMETHING
,这可能不是最方便的方法。 - 如果我
from const import SOMETHING_A, SOMETHING_B ...
,那么显然它太冗长了,并且破坏了结构化的目的。 - 因此,我觉得在这种情况下,
from const import *
做一个可能是一个更好的select。
- 如果我
作为一个testing,我创build了一个带有2个函数A和B的模块test.py,分别打印“A 1”和“B 1”。 导入test.py后:
import test
。 。 。 我可以运行test.A()和test.B()和test.A()这两个函数作为模块在命名空间显示,所以如果我编辑test.py我可以重新加载它:
import importlib importlib.reload(test)
但是,如果我做到以下几点:
from test import *
在命名空间中没有引用“testing”,因此在编辑之后无法重新加载它(据我所知),这是交互式会话中的一个问题。 鉴于以下任一情况:
import test import test as tt
将在名称空间中添加“test”或“tt”(分别)作为模块名称,这将允许重新加载。
如果我做:
from test import *
名字“A”和“B”作为函数出现在名字空间中。 如果我编辑test.py,并重复上面的命令,函数的修改版本不会被重新加载。
以下命令引发错误消息。
importlib.reload(test) # Error - name 'test' is not defined
如果有人知道如何重新加载一个模块加载“从模块导入*”,请张贴。 否则,这将是避免这种forms的另一个原因:
from module import *
这是一个非常糟糕的做法,有两个原因:
- 代码可读性
- 覆盖variables/函数等的风险
对于第一点 :我们来看一个例子:
from module1 import * from module2 import * from module3 import * a = b + c - d
在这里,在看代码的时候,没有人会从b
, c
和d
实际属于哪个模块得到想法。
另一方面,如果你这样做:
# vv will know that these are from module1 from module1 import b, c # way 1 import module2 # way 2 a = b + c - module2.d # ^ will know it is from module2
这对你来说更加清洁,join你的团队的新人也会有更好的想法。
对于第二点 :假设说module1
和module2
variables都是b
。 当我这样做时:
from module1 import * from module2 import * print b # will print the value from module2
这里来自module1
的值会丢失。 这将是很难debugging为什么代码不工作,即使b
在module1
声明,我已经写了代码,期望我的代码使用module1.b
如果你在不同的模块中有相同的variables,并且你不想导入整个模块,你甚至可以这样做:
from module1 import b as mod1b from module2 import b as mod2b