Python导入有哪些好的经验法则?

What are good rules of thumb for Python imports?

我有点困惑,您可以通过多种方式在Python中导入模块。

1
2
3
import X
import X as Y
from A import B

我一直在阅读范围界定和名称空间的相关内容,但我希望得到一些关于什么是最佳策略、在什么情况下以及为什么是最佳策略的实用建议。导入应该发生在模块级别还是方法/函数级别?在__init__.py或模块代码本身中?

我的问题并没有得到"python包——按类导入,而不是文件导入"的真正回答,尽管它显然是相关的。


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

我们将导入放在文件的开头,就在主文件的docstring之后,例如:

1
2
3
4
5
"""
Registry related functionality.
"""

import wx
# ...

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

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

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

1
2
3
from main.core import Exceptions
# ...
raise Exceptions.FileNotFound()

我们很少使用import X as Y,因为它使搜索特定模块或类变得困难。但是,如果您希望导入两个同名但存在于不同模块中的类,例如:

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

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


让我把对话的一部分粘贴到guido van rossum开始的django dev邮件列表上:

[...]
For example, it's part of the Google Python style guides[1] that all
imports must import a module, not a class or function from that
module. There are way more classes and functions than there are
modules, so recalling where a particular thing comes from is much
easier if it is prefixed with a module name. Often multiple modules
happen to define things with the same name -- so a reader of the code
doesn't have to go back to the top of the file to see from which
module a given name is imported.

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

1:http://code.google.com/p/soc/wiki/pythonstyleguide module_and_package_导入


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

只有在遇到名字冲突时才使用import X as Y

当模块用作主模块时,我只使用功能级别的导入来导入所需的内容,例如:

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

高温高压


上面有人说

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

等于

1
import X

import X允许对a-p进行直接修改,而from X import ...创建a-p的副本。对于from X import A..P如果修改了变量,则不会得到更新。如果修改它们,您只修改您的副本,但X知道您的修改。

如果a-p是函数,你就不知道有什么区别。


其他的已经覆盖了这里的大部分内容,但我只是想添加一个案例,在我尝试类或模块的新版本时,我将使用EDOCX1(临时)。

因此,如果我们迁移到一个模块的新实现中,但不想一次性全部削减代码库,我们可以编写一个xyz_new模块,并在迁移的源文件中执行此操作:

1
import xyz_new as xyz

然后,一旦我们切断了整个代码库,我们只需将xyz模块替换为xyz_new模块,并将所有的导入内容改回

1
import xyz


不要这样做:

1
from X import *

除非你绝对确信你将使用该模块中的每一件事。即使如此,你也应该重新考虑使用不同的方法。

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

1
from X import Y

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

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

你明白了。那时候进口就像

1
import X

变得有用。或者如果我不经常使用x中的任何东西。


我通常尝试使用常规的import modulename,除非模块名很长,或者经常使用。

例如,我会……

1
from BeautifulSoup import BeautifulStoneSoup as BSS

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

或…

1
from xmpp import XmppClientBase

…而不是在我只使用xmppclientbase时导入整个xmpp

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

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

1
from my_other_module import main as other_module_main

…不会用我的其他模块的main取代我的main功能

哦,有一件事——不要做from x import *——这让你的代码很难理解,因为你很难看到一个方法是从哪里来的(from x import *; from y import *; my_func()——我的func定义在哪里?)

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

from x import y as z的东西纯粹是为了方便——只要它能让代码更容易读写,就可以使用它!


当您有一个编写良好的库(在Python中有时是这样)时,您应该导入它并将其作为它使用。写得好的图书馆往往会占用它自己的生命和语言,导致令人愉快的阅读代码,在那里你很少参考图书馆。当一个图书馆写得很好的时候,你不应该太频繁地需要重新命名或者其他任何东西。

1
2
3
4
import gat

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

有时不可能这样写,或者你想从你导入的库中提取东西。

1
2
3
4
from gat import Node, SubNode

node = Node()
child = SubNode(node)

有时,对于很多事情都要这样做,如果导入字符串溢出80列,最好这样做:

1
2
3
4
from gat import (
    Node, SubNode, TopNode, SuperNode, CoolNode,
    PowerNode, UpNode
)

最好的策略是将所有这些导入保持在文件的顶部。最好按字母顺序排列,先导入-语句,然后从导入-语句。

现在我告诉你为什么这是最好的会议。

python完全可以有一个自动导入,当在全局命名空间中找不到该值时,可以从主导入中查找该值。但这不是个好主意。我很快解释了原因。除了实现比简单的导入更复杂之外,程序员不会太多地考虑依赖性,并且发现从哪里导入东西应该以其他方式进行,而不仅仅是查看导入。

需要找出出处是人们讨厌"来自……的原因之一。导入*"。但也存在一些需要这样做的坏例子,例如OpenGL包装。

因此,导入定义实际上对于定义程序的依赖性很有价值。这就是你应该如何利用它们的方式。从它们中,您可以快速地检查一些奇怪的函数是从哪里导入的。


我和杰森在一起的事实是不使用

1
from X import *

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

1
from const import *

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

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


如果您对同一个模块/类有不同的实现,那么import X as Y非常有用。

使用一些嵌套的try..import..except ImportError..import可以从代码中隐藏实现。请参见lxml etree导入示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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")