Python import - zhongjiajie/zhongjiajie.github.com GitHub Wiki

python-import

一个关于import的经典引论

Imports are pretty straightforward really. Just remember the following:

import and from xxx import yyy are executable statements. They execute when the running program reaches that line.

If a module is not in sys.modules, then an import creates the new module entry in sys.modules and then executes the code in the module. It does not return control to the calling module until the execution has completed.

If a module does exist in sys.modules then an import simply returns that module whether or not it has completed executing. That is the reason why cyclic imports may return modules which appear to be partly empty.

Finally, the executing script runs in a module named __main__, importing the script under its own name will create a new module unrelated to __main__.

Take that lot together and you shouldn't get any surprises when importing modules.

python结构

python-modules-packages: python的模块化编程(modular programming)主要包括了两个较为重要的概念 modules 以及 packages,模块化编号的目的是将大而复杂的代码切分成多个小而简单的代码,是代码的迭代和维护更加简单,模块编程的优点有:

  • 简单的: 可以编写简单的函数而不是针对庞大的复杂问题
  • 易于维护的: 因为函数是简单的,可以更好的写测试来维护代码的功能
  • 可重复使用的: 其他程序可以将简单的调用模块中的程序,因为他是一个比较抽象的功能而不是具体的实现
  • 更加有范围的: 可以使得类 函数 变量更加有范围

modules

定义了python一些成员的文件

packages

理解import的前提

  • import的工作流程, 拿import abc作为说明
    • The first thing Python will do is look up the name abc in sys.modules. This is a cache of all modules that have been previously imported.
    • If the name isn’t found in the module cache
      • Python will proceed to search through a list of built-in modules. These are modules that come pre-installed with Python and can be found in the Python Standard Library.
      • If the name still isn’t found in the built-in modules, Python then searches for it in a list of directories defined by sys.path. This list usually includes the current directory, which is searched first.
    • When Python finds the module, it binds it to a name in the local scope. This means that abc is now defined and can be used in the current file without throwing a NameError.
  • 如何引用: 引用模块使用import module_namefrom module_name import module_element两条语句都是可行的
  • 引用的结果: 引用了模块后,sys.modules里记录了当前 run time 下所有已经导出的 module, 也就是说在我们使用import的时候,python会先去sys.modules查看将要被引用的模块在不在里面
  • 判断一个模块是否在当前的run time中: "module" in sys.modules
  • 如果 module_name 不在 sys.modules 中,那 import module_name 将会执行:
    • sys.modules[module_name] = [empty pyc file]
    • execute module_name to generate a module_name.pyc file
    • sys.modules[module_name] = module_name.pyc file path
  • 如果 module_name 已经在 sys.moudles 中,那会去 load 对应的 pyc file,但关键就在这里的 pyc 文件,有两种情况:
    • 上面第一步生成的 pyc 文件,大多数循环引用导致 AttributeError 错误的原因; 循环引用就会存在这样的问题 此时因为module_name文件还没有运行完
    • 上面第三部生成的 pyc 文件,正常情况,不会出异常.

import的排序顺序

  • Imports should always be written at the top of the file, after any module comments and docstrings.
  • Imports should be divided according to what is being imported. There are generally three groups:
    • standard library imports (Python’s built-in modules)
    • related third party imports (modules that are installed and do not belong to the current application)
    • local application imports (modules that belong to the current application)
  • Each group of imports should be separated by a blank space.
"""Illustration of good import statement styling.

Note that the imports come after the docstring.

"""

# Standard library imports
import datetime
import os

# Third party imports
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy

# Local application imports
from local_module import local_class
from local_package import local_function

相对引用和绝对引用

相对引用

以资源当前路径为相对路径,通过相对路径引用别的资源,使用.区分相对引用和绝对引用from .some_module import some_class

绝对引用

绝对引用是以当前项目的根为开始,使用全路径的引用from package1 import module1,但是如果路径过长会导致引用的语句过长难以阅读

FAQ

文件名和标准库文件一样

Python ImportError: cannot import name itemgetter: 和python内置模块名冲突,You should rename your file to not conflict with Python's standard library.

循环导入问题(circular import 或者 cyclic import)

理解循环引用的前提

理解import的前提

循环引用的简单例子

例子

假设有如下的两个文件

# cat a.py
import b
print("This is from module a")

# cat b.py
import a
print("This is from module b")

当我们运行a.py时,由于 循环引入 会打印如下内容

'This is from module a'
'This is from module b'
'This is from module a'

它引用的过程如下:

  • The first line is import b. so it will visit module b
  • The first line at module b is import a. so it will visit module a
  • The first line at module a is import b but note that this line won't be executed again anymore, because every file in python execute an import line just for once, it does not matter where or when it is executed. so it will pass to the next line and print This is from module a.
  • After finish visiting whole module a from module b, we are still at module b. so the next line will print This is from module b
  • Module b lines are executed completely. so we will go back to module a where we started module b.
  • import b line have been executed already and won't be executed again. the next line will print This is from module a and program will be finished.

循环导入导致AttributeError错误

这里

假设有两个文件

# cat a.py
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules)
import b
print "a out"
print b.x

# cat b.py
print "b in"
import a
print "b out"
x = 3

运行a.py,参考循环引用的简单例子,输出如下:

a in
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
  File "a.py", line 4, in <module>
    import b
  File "/home/shlomme/tmp/x/b.py", line 2, in <module>
    import a
 File "/home/shlomme/tmp/x/a.py", line 7, in <module>
    print b.x
AttributeError: 'module' object has no attribute 'x'

这个错误的原因是在b.py运行import a时候会执行a.py中的全部内容,但是b.py还没有运行完,所以会导致x=3的初始化没有运行.根据理解import的前提此时的b存在于sys.moudles中,但是指向的是一个空的pyc文件,要等文件运行完b才能更新sys.moudles对应的值

如何避免循环导入问题

解决循环导入问题

  • 延迟导入(lazy import): 即把import语句写在方法或函数里面,将它的作用域限制在局部,这种方法的缺点就是会有性能问题
  • from xxx import yyy 改成 import xxx;xxx.yyy 来访问的形式,这种办法并不能解决所有场景下的问题
  • 合理组织代码: 出现循环import的问题往往意味着代码的布局有问题,可以合并或者分离竞争资源,将循环变成单向
    • 合并就是将循环引用的代码写到一个文件中
    • 分离就是把需要import的资源提取到第三方文件去

项目在Pycharm中运行成功,但是在terminal中报错modulenotfounderror

Pycharm在运行python代码(运行python console)的时候做了一定处理,他将项目的根目录放到了PYTHONPATH中,这样就能import没有在site-package的,自己写的了


⚠️ **GitHub.com Fallback** ⚠️