为什么“import *”不好?
- 2024-11-21 08:33:00
- admin 原创
- 202
问题描述:
建议不要import *
在Python中使用。
谁能告诉我这样做的原因,以便我下次可以避免这样做?
解决方案 1:
因为它将很多东西放入您的命名空间(可能会遮蔽以前导入的其他对象而您对此一无所知)。
因为您不知道到底导入了什么,也无法轻易找到某个东西是从哪个模块导入的(可读性)。
因为您不能使用类似的酷工具
pyflakes
来静态检测代码中的错误。
解决方案 2:
根据Python 之禅:
明确优于隐含。
...这真的无可争辩吗?
解决方案 3:
您没有将其传递**locals()
给函数,是吗?
由于 Python 缺少“include”语句,并且参数self
是显式的,并且范围规则非常简单,因此通常很容易指出一个变量并判断该对象来自哪里——无需阅读其他模块,也无需任何类型的 IDE(由于语言非常动态,因此 IDE 的自省方式受到限制)。
打破import *
了这一切。
此外,它还具有隐藏错误的具体可能性。
import os, sys, foo, sqlalchemy, mystuff
from bar import *
现在,如果 bar 模块具有任何“ os
”、“ mystuff
”等属性,它们将覆盖显式导入的属性,并可能指向完全不同的东西。在 bar 中定义通常是明智的——这说明了将隐式导入什么——但如果不阅读和解析 bar 模块并遵循其__all__
导入,仍然很难追踪对象的来源。当我接手一个项目时,网络是我修复的第一件事。import *
别误会我的意思:如果import *
缺少了它,我会哭着想要它。但必须小心使用它。一个很好的用例是在另一个模块上提供外观接口。同样,使用条件导入语句或函数/类命名空间内的导入需要一点纪律。
我认为在中型到大型项目,或者有多个贡献者的小型项目中,在静态分析方面需要最低限度的卫生处理 - 至少运行 pyflakes 或者更好的是运行正确配置的 pylint - 以便在几种错误发生之前捕获它们。
当然,因为这是 Python —— 请随意打破规则,去探索 —— 但要警惕可能增长十倍的项目,如果源代码缺乏规范,那将是一个问题。
解决方案 4:
这是因为你污染了命名空间。你将导入你自己命名空间中的所有函数和类,这可能会与你自己定义的函数发生冲突。
此外,我认为使用限定名称对于维护任务来说更加清晰;您可以在代码行上看到函数的来源,因此您可以更轻松地检查文档。
在模块 foo 中:
def myFunc():
print 1
在您的代码中:
from foo import *
def doThis():
myFunc() # Which myFunc is called?
def myFunc():
print 2
解决方案 5:
from ... import *
在交互式会话中执行此操作是可以的。
解决方案 6:
假设在名为 foo 的模块中有以下代码:
import ElementTree as etree
然后在您自己的模块中您有:
from lxml import etree
from foo import *
现在您有一个难以调试的模块,它看起来好像包含 lxml 的 etree,但实际上却包含 ElementTree。
解决方案 7:
理解人们在这里提出的有效观点。但是,我确实有一个论点,有时“星号导入”可能并不总是一种坏习惯:
当我想以这样的方式构造我的代码时,所有常量都转到名为的模块
const.py
:
+ 如果我这样做`import const`,那么对于每个常量,我都必须将其称为`const.SOMETHING`,这可能不是最方便的方法。
+ 如果我这样做`from const import SOMETHING_A, SOMETHING_B ...`,那么显然它太冗长并且违背了结构化的目的。
+ 因此,我觉得在这种情况下,做一个`from const import *`可能是一个更好的选择。
解决方案 8:
http://docs.python.org/tutorial/modules.html
请注意,一般来说,
*
从模块或包导入的做法是不受欢迎的,因为它通常会导致代码可读性较差。
解决方案 9:
这些都是很好的答案。我要补充一点,在教新人用 Python 编程时,处理起来import *
非常困难。即使你或他们没有编写代码,它仍然是一个绊脚石。
我教孩子们(大约 8 岁)用 Python 编程来操作 Minecraft。我喜欢为他们提供一个有用的编码环境(Atom 编辑器)并教授 REPL 驱动的开发(通过bpython)。在 Atom 中,我发现提示/完成与 bpython 一样有效。幸运的是,与其他一些统计分析工具不同,Atom 不会被愚弄import *
。
但是,让我们来看这个例子...在这个包装器中,他们打包了from local_module import *
一堆模块,包括这个块列表。让我们忽略命名空间冲突的风险。通过这样做,from mcpi.block import *
他们让这整个晦涩难懂的块类型列表成为您必须查看才能知道哪些可用的东西。如果他们改用from mcpi import block
,那么您可以键入walls = block.
,然后会弹出一个自动完成列表。
解决方案 10:
这是一个非常糟糕的做法,原因有二:
代码可读性
覆盖变量/函数等的风险
对于第 1 点:让我们看一个例子:
from module1 import *
from module2 import *
from module3 import *
a = b + c - d
在这里,看到代码时没有人会知道它实际上属于哪个b
模块。c
`d`
另一方面,如果你这样做:
# v v 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
这对您来说会更清楚,而且加入您团队的新人也会有更好的想法。
对于第 2 点:假设module1
和module2
都有变量b
。当我这样做时:
from module1 import *
from module2 import *
print b # will print the value from module2
这里,来自的值module1
丢失了。很难调试为什么代码不起作用,即使b
在中声明module1
,并且我编写了代码,期望我的代码使用module1.b
如果您在不同的模块中具有相同的变量,并且您不想导入整个模块,您甚至可以执行以下操作:
from module1 import b as mod1b
from module2 import b as mod2b
解决方案 11:
正如文档中所建议的,您(几乎)永远不应该import *
在生产代码中使用。
虽然*
从模块导入很糟糕,但从包导入*可能更糟糕。
默认情况下,from package import *
导入包中定义的任何名称,包括通过前面的语句__init__.py
加载的包的任何子模块。import
如果包的__init__.py
代码定义了一个名为的列表__all__
,则它将被视为遇到时应导入的子模块名称from package import *
列表。
__all__
现在考虑这个例子(假设中没有定义sound/effects/__init__.py
):
# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround
# in your module
from sound.effects import *
最后一条语句将把echo
和surround
模块导入到当前命名空间(可能覆盖以前的定义),因为它们是在执行sound.effects
该语句时在包中定义的。import
解决方案 12:
为了测试,我创建了一个模块 test.py,其中包含两个函数 A 和 B,分别打印“A 1”和“B 1”。使用以下命令导入 test.py:
import test
...我可以将这两个函数作为 test.A() 和 test.B() 运行,“test”会作为命名空间中的模块显示,因此如果我编辑 test.py,我可以使用以下命令重新加载它:
import importlib
importlib.reload(test)
但如果我执行以下操作:
from test import *
命名空间中没有对“test”的引用,因此在编辑后无法重新加载它(据我所知),这在交互式会话中是一个问题。而以下任一情况:
import test
import test as tt
将在命名空间中添加“test”或“tt”(分别)作为模块名称,这将允许重新加载。
如果我这么做:
from test import *
名称“A”和“B”在命名空间中显示为函数。如果我编辑 test.py,并重复上述命令,则不会重新加载修改后的函数版本。
以下命令将引发错误消息。
importlib.reload(test) # Error - name 'test' is not defined
如果有人知道如何重新加载使用“from module import *”加载的模块,请发帖。否则,这将是避免使用以下形式的另一个原因:
from module import *