Python 导入的良好经验法则是什么?

What are good rules of thumb for Python imports?

提问人:Simon 提问时间:10/11/2008 最后编辑:CommunitySimon 更新时间:4/5/2015 访问量:53220

问:

我对在 Python 中导入模块的多种方式感到有些困惑。

import X
import X as Y
from A import B

我一直在阅读有关范围和命名空间的信息,但我想要一些关于什么是最佳策略、在什么情况下以及为什么的实用建议。导入应该在模块级别还是方法/函数级别进行?在模块代码本身中?__init__.py

Python 包 - 按类导入,而不是文件”并没有真正回答我的问题,尽管它显然是相关的。

蟒蛇 python-import

评论

0赞 S.Lott 10/11/2008
请参阅 stackoverflow.com/questions/187453/...
0赞 S.Lott 10/11/2008
请参阅 stackoverflow.com/questions/186472/...

答:

12赞 MvdD 10/11/2008 #1

我通常会在模块级别使用。如果只需要模块中的单个对象,请使用 。import Xfrom X import Y

仅在您遇到名称冲突时使用。import X as Y

当模块用作主模块时,我只在函数级别使用导入来导入我需要的东西,例如:

def main():
  import sys
  if len(sys.argv) > 1:
     pass

HTH型

68赞 Dzinx 10/11/2008 #2

在我们公司的生产代码中,我们尽量遵循以下规则。

我们将导入放在文件的开头,紧跟在主文件的文档字符串之后,例如:

"""
Registry related functionality.
"""
import wx
# ...

现在,如果我们导入的类是导入模块中为数不多的类之一,我们直接导入名称,因此在代码中我们只需要使用最后一部分,例如:

from RegistryController import RegistryController
from ui.windows.lists import ListCtrl, DynamicListCtrl

但是,有些模块包含数十个类,例如所有可能的异常列表。然后我们导入模块本身并在代码中引用它:

from main.core import Exceptions
# ...
raise Exceptions.FileNotFound()

我们尽可能少地使用 ,因为它使搜索特定模块或类的用法变得困难。但是,有时,如果您希望导入两个具有相同名称但存在于不同模块中的类,则必须使用它,例如:import X as Y

from Queue import Queue
from main.core.MessageQueue import Queue as MessageQueue

作为一般规则,我们不会在方法内部进行导入——它们只会使代码变慢且可读性降低。有些人可能会发现这是轻松解决循环导入问题的好方法,但更好的解决方案是代码重组。

评论

6赞 kmonsoor 6/8/2015
“他们只是让代码变慢”并非如此。它已经在这里进行了测试:stackoverflow.com/a/4789963/617185
0赞 olt 10/11/2008 #3

如果您对同一模块/类有不同的实现,则该功能很有用。import X as Y

使用一些嵌套的 s,您可以在代码中隐藏实现。请参阅 lxml etree 导入示例try..import..except ImportError..import

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
2赞 dbr 10/11/2008 #4

我一般尽量使用常规的,除非模块名称很长,或者经常使用。import modulename

例如,我会做..

from BeautifulSoup import BeautifulStoneSoup as BSS

..所以我可以代替soup = BSS(html)BeautifulSoup.BeautifulStoneSoup(html)

或。。

from xmpp import XmppClientBase

..而不是在我只使用 XmppClientBase 时导入整个 xmpp

如果您想导入很长的方法名称,或者防止破坏现有的导入/变量/类/方法(您应该尝试完全避免这种情况,但并不总是可行的),使用非常方便import x as y

假设我想从另一个脚本运行一个 main() 函数,但我已经有一个 main() 函数。

from my_other_module import main as other_module_main

..不会用my_other_module的函数替换我的函数mainmain

哦,有一件事 - 不要做 - 它使你的代码很难理解,因为你不容易看到方法来自哪里( - my_func在哪里定义?from x import *from x import *; from y import *; my_func()

在所有情况下,你都可以做,然后做......import modulenamemodulename.subthing1.subthing2.method("test")

这些东西纯粹是为了方便 - 只要它能使你的代码更容易阅读或编写,就使用它!from x import y as z

评论

2赞 MestreLion 4/14/2012
恕我直言,这里有一些误解......1)“当我只使用...时导入整个xmpp”:这错误地暗示这种方法“更轻”,但无论你导入了多少对象,Python 无论如何都会加载并初始化整个模块。至于“命名空间污染”,两者都会在本地命名空间中添加一个条目:或 .所以这个理由是站不住脚的xmppXmppClientBase
2赞 MestreLion 4/14/2012
2)“因为你不容易看出一个方法来自哪里”:对于*导入是正确的,但对于你只用来缩短模块/对象名称的方法也是如此。从何而来?使用也会遭受(一点)这样的影响:。现在前方 300 行你看......现在,MyClass 又从何而来?读者被迫始终返回标题以查看从何处导入对象asBSS()fromfrom mymod import MyClassclass MyOtherClass(MyClass):
5赞 davidavr 10/11/2008 #5

其他人已经涵盖了这里的大部分内容,但我只想添加一个案例,当我尝试一个类或模块的新版本时,我将(暂时)使用。import X as Y

因此,如果我们要迁移到模块的新实现,但不想一次切断所有代码库,我们可能会编写一个模块并在我们迁移的源文件中执行此操作:xyz_new

import xyz_new as xyz

然后,一旦我们切入整个代码库,我们只需将模块替换为并将所有导入更改回xyzxyz_new

import xyz
3赞 Jason Baker 10/11/2008 #6

别这样:

from X import *

除非你绝对确定你会使用该模块中的每一个东西。即便如此,您可能也应该重新考虑使用不同的方法。

除此之外,这只是一个风格问题。

from X import Y

很好,可以为您节省大量打字时间。当我经常使用它时,我倾向于使用它,但是如果您从该模块导入大量内容,则最终可能会得到如下所示的 import 语句:

from X import A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P

你明白了。那时进口喜欢

import X

变得有用。要么是这样,要么是我并没有经常在 X 中使用任何东西。

评论

0赞 John Fouhy 10/13/2008
'from X import *' 确实有它的用途 -- 例如,使用 pyparsing。
0赞 Jason Baker 10/13/2008
我同意。但总的来说,这不是一个好习惯。没有什么比必须追踪什么函数来自哪个模块更让我烦恼的了,因为有很多来自 x import * 类型的语句。
40赞 Bartosz Ptaszynski 10/12/2008 #7

让我在 Guido van Rossum 发起的 django-dev 邮件列表上粘贴一段对话:

[...] 例如,它是 Google Python 风格指南[1] 的一部分,所有 导入必须导入模块,而不是从中导入类或函数 模块。类和函数的数量比实际数量多得多 模块,所以回忆一个特定的东西来自哪里是很重要的 如果它以模块名称为前缀,则更容易。通常有多个模块 碰巧用相同的名称定义事物 -- 所以代码的读者 不必返回文件顶部即可查看从哪个 导入给定名称的模块。

来源: http://groups.google.com/group/django-developers/browse_thread/thread/78975372cdfb7d1a

1:http://code.google.com/p/soc/wiki/PythonStyleGuide#Module_and_package_imports

评论

1赞 Priidu Neemre 10/8/2014
IMO 这应该是导入事物的首选方式,尤其是在大型项目中。
1赞 Cheery 10/15/2008 #8

当你有一个写得很好的库时,这在 python 中有时是这种情况,你应该导入它并使用它。写得好的库往往会有自己的生命和语言,从而产生令人愉悦的代码,而你很少引用库。当一个库写得很好时,你不应该经常需要重命名或其他任何事情。

import gat

node = gat.Node()
child = node.children()

有时不可能以这种方式编写它,或者你想从你导入的库中取出东西。

from gat import Node, SubNode

node = Node()
child = SubNode(node)

有时你会为很多事情这样做,如果你的导入字符串溢出了 80 列,这样做是个好主意:

from gat import (
    Node, SubNode, TopNode, SuperNode, CoolNode,
    PowerNode, UpNode
)

最好的策略是将所有这些导入保留在文件的顶部。最好按字母顺序排序,首先是 import -statements,然后是 from import -statements。

现在我告诉你为什么这是最好的约定。

Python 完全可以有一个自动导入,当无法从全局命名空间中找到该值时,它会从主导入中查找该值。但这不是一个好主意。我简要解释一下原因。除了实现它比简单的导入更复杂之外,程序员不会过多地考虑问题,并找出从哪里导入的东西应该以其他方式完成,而不仅仅是查看导入。

需要找出缺陷是人们讨厌“来自......导入 *”。不过,存在一些需要执行此操作的坏例子,例如 opengl -wrappings。

因此,导入定义实际上很有价值,因为它定义了程序的缺点。这是您应该如何利用它们的方式。从中,您可以快速检查从哪里导入一些奇怪的函数。

0赞 CastleDweller 2/10/2010 #9

我和杰森在一起,因为没有使用

from X import *

但就我而言(我不是专业的程序员,所以我的代码不太符合编码风格)我通常会在我的程序中做一个包含所有常量的文件,如程序版本、作者、错误消息和所有这些东西,所以文件只是定义,然后我进行导入

from const import *

这为我节省了很多时间。但它是唯一具有该导入的文件,这是因为该文件中的所有文件都只是变量声明。

在具有类和定义的文件中执行这种导入可能很有用,但是当您必须读取该代码时,您将花费大量时间来查找函数和类。

11赞 Robert Jacobs 4/18/2013 #10

上面有人说

from X import A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P

相当于

import X

import X允许直接修改 A-P,同时创建 A-P 的副本。因为如果变量被修改,则不会获得变量的更新。如果你修改它们,你只修改你的副本,但 X 确实知道你的修改。from X import ...from X import A..P

如果 A-P 是函数,您将不知道其中的区别。

评论

0赞 RemcoGerlich 12/17/2013
特别是,如果你想在单元测试中使用模拟 say ,那么这只有在你使用 .mockX.Aimport X