为什么“进口*”不好?

时间:2010-03-05 12:40:42

标签: python python-import

建议不要在Python中使用import *

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

12 个答案:

答案 0 :(得分:192)

  • 因为它会在你的命名空间中放入很多东西(可能会影响之前导入的其他一些对象,你不会知道它)。

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

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

答案 1 :(得分:40)

根据Zen of Python

  

明确比隐含更好。

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

答案 2 :(得分:36)

您没有将**locals()传递给函数,是吗?

由于Python缺少“include”语句, self参数是显式的,作用域规则非常简单,通常很容易指出一个变量的指针,告诉那个对象来自哪里 - 没有阅读其他模块,也没有任何类型的IDE(无论如何,这些都是内省的,因为语言是非常动态的。)

import *打破了这一切。

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

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

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

不要误解我:如果遗失import *,我会哭的。但必须谨慎使用。一个很好的用例是在另一个模块上提供外观界面。 同样,使用条件导入语句或在函数/类命名空间内导入需要一些纪律。

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

当然,因为这是python - 随意破坏规则和探索 - 但要警惕可能增长十倍的项目,如果源代码缺少纪律,那将是一个问题。

答案 3 :(得分:15)

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

此外,我认为使用合格的名称对于维护任务更为明确;你可以在代码行上看到函数的来源,这样你就可以更容易地查看文档了。

在模块foo中:

def myFunc():
    print 1

在您的代码中:

from foo import *

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

def myFunc():
    print 2

答案 4 :(得分:15)

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

答案 5 :(得分:10)

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

  

请注意,一般来说,从模块或包中导入*的做法是不受欢迎的,因为它经常导致可读性差的代码

答案 6 :(得分:7)

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

import ElementTree as etree

然后在你自己的模块中你有:

from lxml import etree
from foo import *

你现在有一个难以调试的模块看起来像它有lxml的etree,但实际上却有ElementTree。

答案 7 :(得分:6)

这些都是很好的答案。我要补充说,在用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.,然后会弹出自动填充列表。 Atom.io screenshot

答案 8 :(得分:4)

理解人们放在这里的有效点。但是,我确实有一个论点,有时候,“明星导入”可能并不总是一个坏习惯:

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

答案 9 :(得分:2)

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

  1. 代码可读性
  2. 覆盖变量/功能等的风险
  3. 第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的值丢失了。即使在b中声明了module1并且我编写了期望我的代码使用module1.b

    的代码,也很难调试代码无法正常工作的原因

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

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

答案 10 :(得分:2)

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

import test

。 。 。我可以运行2个函数作为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 *

答案 11 :(得分:1)

按照文档中的建议,永远不要在生产代码中使用import *

虽然从模块导入*不好,但importing * from a package却更糟。基本上,from package import *导入由包的__init__.py定义的任何名称,但是它还包括以前的import语句加载的包的任何子模块。

考虑以下示例:

# 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时定义的语句已执行。