Importing Python Modules

简介

import 和 from-import 对于Python新手经常带来严重的混淆,不过相信大家一旦清楚它们的实现机理后就不会对使用他们有任何困惑了。
这篇文章将努力理清import和from-import 相关的一切。

import modules的几种方式

Python提供至少三种方式来import modules。使用import、from或者内置的__import__function,篇幅所限本文不讨论其他的非主流方式。
下面是这几种方式的实现原理:
  • import X 导入module X,并且在当前命名空间创建到X的引用。换言之,import X后就可以使用X.name使用模块X中的东东了。
  • from X import * 导入module X,并且在当前命名空间创建到X中所有public对象(即除去名称以"_”开头的所有对象)的引用。亦即执行这条语句后,可以直接使用名字使用module X中的东西。但是因为X自身是未定义的,所以无法使用X.name。命名重复时会使用较新的版本,如果X中该名称已经指向其他对象你的模块也不会察觉。这也是该方法的危险之处,虽然用起来方便但容易不知觉间产生BUG。
  • from X import a, b, c 导入module X,并且在当前命名空间创建给定对象的引用,现在可以直接使用a、b和c了。
  • X = __import__(‘X’) 与import X比较相似,不同之处在于:1)可以使用一个string传递module的名字 2)可在当前命名空间将其赋值给一个变量(这在导入的module名称不确定或希望动态导入module时非常有用)

应该采用哪一种?

简而言之:尽量用import。
以下几处情形例外:
  • Module文档要求使用from-import。常见的例子是Tkinter,其被设计为仅向当前命名空间添加widget classes和相关的常量。如果使用import Tkinter会使你的代码可读性很差,故通常不推荐这样做。
  • 导入一个包中的组件。当只需要导入包中一个特定的子module时,使用from io.drivers import zip通常要比import io.drivers.zip方便一些,因为前者可以让我们简单地用zip就可以使用这个module,而不是使用它的全名io.drivers.zip。在这种情况下,from-import起到的作用很像简化的import,而且没有什么命名混淆的风险。
  • 在执行前不清楚module的名字。在这种情况下应该使用__import__(module_name),其中module_name是Python string,这样可以在变更module名称时不需要改动代码。
  • 对自己将做什么非常清楚。如果确认如此那尽可以去用from import,但是一定要三思而后行;-)

Python是如何导入module的?

当Python导入module时,它首先检查module注册表(sys.modules)确认是否该module已经被导入过,如果存在就使用已导入的module代替它。
否则,Python将执行以下步骤:
  1. 创建一个新的空module对象(其本质是一个dictionary)
  2. 将该module对象插入到sys.modules dictionary中
  3. 加载module的代码对象(如果需要会先编译这个module)
  4. 在新module的命名空间执行该module的代码对象,代码中赋值的所有变量在该module对象里可用。通过该module对象中Execute the module code object in the new module’s namespace. All variables assigned by the code will be available via the module object.
这意味着导入一个已经加载的module性能消耗是非常小的,Python只需要在dictionary中查找下module的名字就可以了。

其他要点

使用module作为脚本

如果你将一个module作为脚本运行,即直接将其名称给编译器而非导入它,那它将以名称__main__加载。
如果稍后在程序中加载同一个module,它将被重新加载并以其真实的名称重新执行,所以如果不细心的话可能会做两次同样的事。

循环导入

在Python中,像def、class和import之类的语句都是声明。
module在导入时被执行,但新的函数和类并不会添加到module的命名空间中直到执行def或class进行声明,这在循环导入会有很明显的影响。
比如module X导入module Y并定义一个函数spam:
    # module X
    import Y
    def spam():
        print "function in module x"
如果从主程序中导入X,Python将加载X的代码并执行它。当Python到import Y这一行声明时,它加载Y的代码并转而执行Y的代码。
此时,Python已经在sys.modules中加载了X和Y。但X还不包含任何东西,def spam这行声明还未执行。
现在如果Y导入X(出现循环导入),它将得到一个指向空module X对象的引用,但如果试图调用函数X.spam将会失败,因为此时虽然存在X但并不存在X.spam:
    # module Y
    from X import spam # doesn't work: spam isn't defined yet!
这个问题和使用from-import无关,即使换用import也会如此:
    # module Y
    import X
    X.spam() # doesn't work either: spam isn't defined yet!
解决的方法就是重构代码来避免循环导入,通常可以通过将部分内容拆分到单独的module来解决。这种解决方式类似于C++中解决循环引用时使用前置声明将声明和定义分离的方法,即将冲突的执行代码分拆到其他文件。
或者将import移到module的末尾,比如上面的例子如果将import Y移到module X的末尾就可以一切正常了,但其实并不总奏效,万一X.spam使用过Y就又出错了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章