为啥“import *”不好?

Posted

技术标签:

【中文标题】为啥“import *”不好?【英文标题】:Why is "import *" bad?为什么“import *”不好? 【发布时间】:2011-01-24 02:37:29 【问题描述】:

建议不要在 Python 中使用import *

谁能分享一下原因,这样我下次就可以避免了?

【问题讨论】:

这取决于您是在编写脚本还是编写需要重用的代码。有时忽略代码标准是值得的。如果你有一个明确的命名约定,那么“import *”也可以。例如"从猫导入 *;TabbyCat;MaineCoonCat;CalicoCat;" import * 在 Python 2 或 3 中首先对我不起作用。 这能回答你的问题吗? What exactly does "import *" import? 一个更微妙的版本是来自 C# 的 using package.module;,尽管它在编译时通常有一两个以上的保护措施。 【参考方案1】:

因为它会将很多东西放入您的命名空间(可能会影响之前导入的其他对象,而您不会知道它)。

因为您不确切知道导入的内容,并且无法轻松找到从哪个模块导入了某个内容(可读性)。

因为您不能使用像 pyflakes 这样的酷工具来静态检测代码中的错误。

【讨论】:

是的,当有人使用 * import 时,我真的很讨厌我的工作,因为那样我就不能只运行 pyflakes 并且开心,而是必须修复这些导入。不过很好,使用 pyflakes 可以帮助我:-) 举个具体的例子,很多 NumPy 用户在使用 from numpy import * 或“有用”工具为他们做这件事时都被numpy.any 遮蔽any 所困扰。 出于同样的原因,我是否应该避免对 IPython 使用 --pylab 开关? 为了突出我在阅读本文之前从未想过的风险(“可能会影响以前导入的其他对象”):import * 使 @ 的 order 987654326@ 声明很重要......即使对于通常不关心导入顺序的标准库模块也是如此。当进口战争的前受害者成为唯一的幸存者时,像按字母顺序排列你的 import 语句这样无辜的事情可能会破坏你的脚本。 (即使您的脚本现在可以工作并且永远不会更改,但如果导入的模块引入了一个新名称来替换您所依赖的名称,它可能会在稍后的某个时间突然失败。) 我实际上来到这里想知道为什么我看到的所有 GIS 参考代码(以及许多其他参考代码,甚至是教程),人们“导入 ”,然后只导入他们想要使用的子模块“ from import ”,他们从不使用 . 然后他们直接使用子模块。我到处都看到这种情况,难道人们只是不知道 Python 导入是如何工作的吗?【参考方案2】:

根据Zen of Python:

显式优于隐式。

...当然不能与之争辩?

【讨论】:

【参考方案3】:

您不会将**locals() 传递给函数,对吗?

由于 Python 缺少 "include" 语句, self 参数是显式的, 范围规则非常简单,通常很容易指责在一个变量上,并告诉该对象来自哪里——无需读取其他模块,也无需任何类型的 IDE(无论如何,由于语言非常动态,因此自省方式受到限制)。

import * 打破了这一切。

此外,它还具有隐藏错误的具体可能性。

import os, sys, foo, sqlalchemy, mystuff
from bar import *

现在,如果 bar 模块具有“os”、“mystuff”等属性中的任何一个,它们将覆盖显式导入的属性,并可能指向非常不同的东西。在 bar 中定义 __all__ 通常是明智的——这说明了将隐式导入的内容——但如果不读取和解析 bar 模块并遵循 its 导入,仍然很难追踪对象的来源。当我拥有一个项目的所有权时,import * 的网络是我修复的第一件事。

别误会我:如果import * 不见了,我会哭着得到它。但必须小心使用。一个好的用例是在另一个模块上提供外观接口。 同样,使用条件导入语句或在函数/类命名空间中导入也需要一些纪律。

我认为在大中型项目或有多个贡献者的小型项目中,在静态分析方面需要最低限度的卫生 - 至少运行 pyflakes 甚至更好地配置正确配置的 pylint - 以捕获几种发生之前的错误。

当然,既然这是 python——随意打破规则,去探索——但要警惕可能增长十倍的项目,如果源代码缺乏纪律,那将是一个问题。

【讨论】:

Python 2.x 确实有一个“include”语句。它被称为execfile()。幸运的是,它在 3.x 中很少使用并且消失了。 如果被调用的函数在另一个文件中,**vars() 包含全局变量怎么样? :P【参考方案4】:

那是因为你污染了命名空间。您将在自己的命名空间中导入所有函数和类,这可能与您自己定义的函数发生冲突。

此外,我认为使用限定名称对维护任务更清晰;您可以在代码行本身上看到函数的来源,因此您可以更轻松地查看文档。

在模块 foo 中:

def myFunc():
    print 1

在您的代码中:

from foo import *

def doThis():
    myFunc() # Which myFunc is called?

def myFunc():
    print 2

【讨论】:

【参考方案5】:

可以在交互式会话中使用from ... import *

【讨论】:

doctest 字符串中怎么样?在这种情况下,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 * 可能是更好的选择。

【讨论】:

同意这个。特别是通过使用大写字母会更容易理解这个变量是const.py中定义的常量之一【参考方案8】:

http://docs.python.org/tutorial/modules.html

请注意,通常不赞成从模块或包中导入* 的做法,因为它通常会导致代码可读性差

【讨论】:

【参考方案9】:

这些都是很好的答案。我要补充一点,在教新人用 Python 编码时,处理import * 非常困难。即使你或他们没有编写代码,它仍然是一个绊脚石。

我教孩子(大约 8 岁)用 Python 编程来操作 Minecraft。我喜欢为他们提供一个有用的编码环境(Atom Editor)并教授 REPL 驱动的开发(通过bpython)。在 Atom 中,我发现提示/完成与 bpython 一样有效。幸运的是,与其他一些统计分析工具不同,Atom 不会被import * 愚弄。

但是,让我们举这个例子......在this wrapper 他们from local_module import * 一堆模块,包括this list of blocks。让我们忽略命名空间冲突的风险。通过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

在这里,看到代码没有人会知道bcd 究竟属于哪个模块。

另一方面,如果你这样做:

#                   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 点:假设module1module2 都有变量b。当我这样做时:

from module1 import *
from module2 import *

print b  # will print the value from module2

这里来自module1 的值丢失了。即使在module1 中声明了b 并且我已经编写了期望我的代码使用module1.b 的代码,也很难调试为什么代码不起作用

如果您在不同的模块中有相同的变量,并且您不想导入整个模块,您甚至可以这样做:

from module1 import b as mod1b
from module2 import b as mod2b

【讨论】:

【参考方案11】:

作为测试,我创建了一个包含 2 个函数 A 和 B 的模块 test.py,它们分别打印“A 1”和“B 1”。导入 test.py 后:

import test

。 . .我可以将 2 个函数作为 test.A() 和 test.B() 运行,并且“test”在命名空间中显示为 module,所以如果我编辑 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 *

【讨论】:

【参考方案12】:

按照文档中的建议,您应该(几乎)永远不要在生产代码中使用 import *

虽然从 模块 导入 * 很糟糕,但 importing * from a package 可能更糟糕。

默认情况下,from package import * 导入包的__init__.py 定义的任何名称,包括由之前的import 语句加载的包的任何子模块。

如果包的__init__.py 代码定义了一个名为__all__ 的列表,则它被视为遇到from package import * 时应导入的子模块名称 的列表。

现在考虑这个例子(假设sound/effects/__init__.py 中没有定义__all__):

# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround

# in your module
from sound.effects import *

最后一条语句会将echosurround 模块导入当前命名空间(可能会覆盖以前的定义),因为它们是在执行import 语句时在sound.effects 包中定义的。

【讨论】:

以上是关于为啥“import *”不好?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的js文件import 语法还报错

为啥不建议@import 链接css?

为啥我不能使用 BabelJS 使用“import ... as”导入默认导出

为啥我的 import.sql 文件没有自动运行?

pycharm编写python,图中import MySQLdb没问题,import mysql.connector出错,为啥会出错?

在vue中import()语法为啥不能传入变量