为什么“导入*”不好?


152

建议不要import *在Python中使用。

任何人都可以分享原因,以便下次可以避免吗?



2
这取决于您是脚本编写还是编写代码,需要重用。有时值得忽略代码标准。如果您有一个命名约定,可以清楚地说明东西的来源,那么“ import *”也可以。例如“从Cats import *; TabbyCat; MaineCoonCat; CalicoCat;”
gatoatigrado 2010年

3
import *不适用于Python 2或3的我
。– joshreesjones

1
这回答了你的问题了吗?“ import *”导入到底是什么?
AMC

Answers:


222
  • 因为它在名称空间中放入了很多东西(可能会遮盖以前导入的其他对象,所以您将一无所知)。

  • 因为您不完全知道要导入的内容,而且不容易找到从哪个模块导入的内容(可读性)。

  • 因为您不能使用像pyflakes静态检测代码中的错误之类的出色工具。


2
是的,当有人使用* import时,我真的很讨厌我的工作,因为那样我就不能只运行pyflakes并感到高兴,而必须修复这些导入。很好,但是pyflakes可以帮助我:-)
gruszczy 2010年

6
举一个具体的例子,许多NumPy用户在这样做时被numpy.any阴影遮住了anyfrom numpy import *或者“帮助”工具为他们做了阴影。
user2357112支持Monica 2014年

1
出于相同的原因,我应该避免对IPython使用--pylab开关吗?
timgeb 2014年

6
为了突出阅读这之前,我从来没有想过的风险(“可能黑影从以前进口的一些其他对象”):import *使订单中的import显著报表......即使是通常不关心进口秩序标准库模块。import当以前的进口战争中的伤亡者成为唯一幸存者时,像按字母顺序排列的陈述那样无辜的行为可能会破坏您的脚本。(即使您的脚本现在可以正常工作并且永远不会更改,如果导入的模块引入了一个新名称来替换您所依赖的名称,它可能会在以后的某个时间突然失败。)
Kevin J. Chase

48

根据Python禅宗

显式胜于隐式。

...不能肯定地说吗?


28
实际上,您可以对此进行争论。鉴于您没有在Python中显式声明变量,因此它们也完全不一致,一旦将其分配给变量,它们便会存在。
康拉德·鲁道夫

7
@gruszczy:声明变量对于什么是多余的?分配?不,这是两个独立的概念,声明某些内容可以传达非常独特且重要的信息。无论如何,显性总是与冗余联系在一起,它们是同一枚硬币的两个面。
康拉德·鲁道夫

3
@kriss对,但这不是我的意思。我的观点是,未明确声明变量会导致错误。你说“没有[声明]的转让是不可能的”。但这是错误的,我的意思是,不幸的是,Python使这一切成为可能。
康拉德·鲁道夫2014年

3
@kriss 声明提供给编译器的一条信息是您确实打算声明一个新变量。这是类型系统的关键信息。您说现代IDE解决了迷惑性问题,但这完全是错误的,实际上,这在非静态编译语言中是一个实质性问题,这就是Perl添加use strict(JavaScript var)的原因。顺便说一句,Python当然不是无类型的(实际上是强类型的)。无论如何,即使如果你是正确的,这将仍然矛盾的Python的禅,引用这个答案。
康拉德·鲁道夫2014年

3
@kriss您错了:重复使用相同的变量名称不是问题-重复使用相同的变量(即,在相同范围内使用相同的名称)。显式声明将完全避免此错误(以及其他基于简单错误输入的错误,正如我已经说过的那样,实际上这是一个极为常见且耗时的问题,即使您说对了,类似Perl的问题更大)语言)。我所暗示的矛盾是禅宗对明晰性的要求,这是在这里自然地抛在了窗外的。
Konrad Rudolph

40

您不传递**locals()给函数,是吗?

因为Python缺少“包括”语句,并且self参数是明确的,而且范围规则相当简单,它通常很容易在一个变量指向一个手指,告诉哪里该对象来自-没有阅读其他模块,无任何IDE(由于语言是非常动态的,因此反省自省的方式)。

import *休息了这一切。

而且,它有隐藏错误的具体可能性。

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

现在,如果bar模块具有“ os”,“ mystuff”等属性中的任何一个,它们将覆盖显式导入的属性,并可能指向非常不同的事物。__all__在bar中定义通常是很明智的-这说明了要隐式导入的内容-但是,如果不读取和解析bar模块并跟随导入,仍然很难跟踪对象的来源。import *获得项目所有权时,首先要解决的网络问题。

不要误会我:如果import *丢失了,我会哭着拥有它。但是必须谨慎使用。一个好的用例是在另一个模块上提供Facade接口。同样,使用条件导入语句或在函数/类名称空间中进行导入也需要一些纪律。

我认为在大中型项目或具有多个贡献者的小型项目中,就静态分析而言,至少需要保持卫生(至少运行pyflakes甚至更好地配置正确的pylint)才能捕获多种错误。他们发生了。

当然,由于这是python-可以随意打破规则并进行探索-但要警惕可能增长十倍的项目,如果源代码缺少规范,那将是一个问题。


6
Python 2.x 确实有一个“ include”语句。叫做execfile()。幸运的是,它很少在3.x中使用和使用。
Sven Marnach

**vars()如果被调用函数在另一个文件中,如何包含全局变量?:P
Solomon Ucko '18

16

可以from ... import *在交互式会话中进行。


doctest字符串里面怎么样?import *在这种情况下,get 是否会在“沙箱”中解释?谢谢。
PatrickT

16

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

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

在模块foo中:

def myFunc():
    print 1

在您的代码中:

from foo import *

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

def myFunc():
    print 2


9

假设您在名为foo的模块中具有以下代码:

import ElementTree as etree

然后在您自己的模块中,您可以:

from lxml import etree
from foo import *

现在,您有了一个难以调试的模块,看起来其中包含lxml的etree,但实际上是ElementTree。


7

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

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

但是,让我们举个例子...在这个包装器中,他们from local_module import *是一堆模块,其中包括这个块列表。让我们忽略名称空间冲突的风险。通过这样做,您from mcpi.block import *就可以使晦涩难懂的块的整个列表成为必需的东西,以便您了解可用的块。如果他们改用from mcpi import block,则可以键入walls = block.,然后会弹出一个自动完成列表。 Atom.io屏幕截图


6

了解了人们在这里提出的有效观点。但是,我确实有一个论点,有时“星号导入”可能并不总是一个坏习惯:

  • 当我想以所有常量都进入名为的模块的方式构造代码时const.py
    • 如果我这样做import const,那么对于每个常量,我都必须将其称为const.SOMETHING,这可能不是最方便的方法。
    • 如果我这样做了from const import SOMETHING_A, SOMETHING_B ...,那么显然它太冗长而无法达到结构化的目的。
    • 因此,我觉得在这种情况下,执行a from const import *可能是更好的选择。

4

这是非常糟糕的做法,原因有两个:

  1. 代码可读性
  2. 覆盖变量/功能等的风险

对于第1点:让我们看一个例子:

from module1 import *
from module2 import *
from module3 import *

a = b + c - d

在这里,看到代码,没有人会得到关于从哪个模块的想法bc并且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变量as设置为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

2

作为测试,我创建了一个模块test.py,其中包含2个函数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 *

在命名空间中没有对“测试”的引用,因此在编辑后(据我所知),没有办法重新加载它,这在交互式会话中是一个问题。而以下任何一项:

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 *

2

如文档中所建议,您(几乎)永远不要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 *

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.