为什么"进口*"不好?

Sof*_*tic 136 python python-import

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

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

gru*_*czy 203

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

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

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

  • +1因为你比我快,还添加了pyflakes参数.应该是+2但是我不能给那个:) (12认同)
  • 作为一个具体的例子,NumPy的许多用户被'numpy.any`影响`any`时,他们来自numpy import*`或者"有用"的工具为他们做了. (6认同)
  • 为了突出我在阅读之前从未想过的风险("可能会影响先前导入的其他一些对象"):`import*`使`import`语句的_order_显着...即使对于标准库模块也是如此通常关心进口订单.当进口战争的前牺牲者成为唯一的幸存者时,像你的"进口"声明按字母顺序排列的无辜可能会破坏你的剧本.(即使您的脚本现在可以正常工作而且永远不会更改,如果导入的模块引入了一个替换您所依赖的新名称,它可能会在以后的某个时间突然失败.) (6认同)
  • 是的,当有人使用*import时,我真的很讨厌我的工作,因为那时我不能只运行pyflakes并且开心,但必须修复那些进口.不过很好,那个pyflakes帮助我:-) (2认同)

Fed*_*rer 45

根据Python禅宗:

显式优于隐式.

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

  • 实际上,你*可以*争论.它也是完全不一致的,因为你没有在Python中显式地声明变量,只要你分配它们就会弹出它们. (19认同)
  • @gruszczy:声明变量是多余的*什么*?分配?不,这是两个独立的概念,并宣称某些东西传达了一个非常独特和重要的信息.无论如何,显性总是与冗余有关,它们是同一枚硬币的两面. (7认同)
  • @kriss是对的,但那不是我的观点.我的观点是,未明确声明变量会导致错误.你说"没有[声明]的任务是不可能的".但那是错的,我的全部观点是,遗憾的是Python正是这样做的. (3认同)
  • @kriss*声明给编译器的另一条*信息是你确实打算声明一个新变量.这是类型系统的*关键*信息.你说现代的IDE解决了错误输入问题,但这完全是错误的,事实上这在非静态编译语言中是一个*实质性问题,这就是为什么Perl添加了`use strict`(JavaScript`var`).顺便说一句,当然Python不是*无类型的(事实上它是强类型的).无论如何,即使*如果你是对的,这仍然会与这个答案中引用的Python的禅相矛盾. (3认同)
  • @kriss你错了:重用相同的变量*name*不是问题 - 重用相同的*变量*(即同一范围内的同名).明确的声明可以防止这个错误(和其他人一样,基于简单的错误输入,正如我所说的那样,实际上这是一个非常常见和耗时的问题,即使你认为问题在Perl中更大是正确的语言).而我提到的矛盾是禅宗对显性的要求,在这里身体被抛出窗外. (3认同)

Mar*_*ani 38

你没有传递**locals()给函数,对吗?

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

import *休息了这一切.

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

import os, sys, foo, sqlalchemy, mystuff
from bar import *
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 我喜欢你的态度. (12认同)
  • Python 2.x*确实*有一个"include"语句.它被称为`execfile()`.幸运的是,它很少使用并且在3.x中消失了. (6认同)

ext*_*eon 16

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

此外,我认为使用合格的名称对于维护任务更为明确; 您可以在代码行上看到函数的来源,因此您可以更轻松地查看文档.

在模块foo中:

def myFunc():
    print 1
Run Code Online (Sandbox Code Playgroud)

在你的代码中:

from foo import *

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

def myFunc():
    print 2
Run Code Online (Sandbox Code Playgroud)


cod*_*ape 15

可以from ... import *在交互式会话中完成.


Fel*_*ing 10

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

请注意,一般来说,*从模块或包导入的做法是不受欢迎的,因为它通常会导致代码难以理解.


jcd*_*yer 8

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

import ElementTree as etree
Run Code Online (Sandbox Code Playgroud)

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

from lxml import etree
from foo import *
Run Code Online (Sandbox Code Playgroud)

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


Bru*_*sky 7

这些都是很好的答案.我要补充一点,在教新人用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.,然后会弹出一个自动完成列表. Atom.io截图


ibi*_*bic 6

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

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


Moi*_*dri 5

这是一种非常糟糕的做法,原因有二:

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

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

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

a = b + c - d
Run Code Online (Sandbox Code Playgroud)

在这里,看到代码,没有人会得到关于从哪个模块的想法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
Run Code Online (Sandbox Code Playgroud)

它对你来说更干净,而且新加入你的团队的人会有更好的想法。

对于第2点:假设都module1module2有变量b。当我做:

from module1 import *
from module2 import *

print b  # will print the value from module2
Run Code Online (Sandbox Code Playgroud)

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

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

from module1 import b as mod1b
from module2 import b as mod2b
Run Code Online (Sandbox Code Playgroud)