这个 Git 仓库通过一些简单的 demo 来探讨 Python import 机制,建议同步阅读一下。
import <>只支持绝对路径的 import,相对路径的 import 必须使用from <> import
- 根据PEP-0328 关于 “Absoulte Imports” 的描述,为消除二义性,Python 推荐使用绝对路径的 import,并约定其语句格式为
import <>。
To resolve the ambiguity in
import foo, it is proposed that foo will always be a module or package reachable from sys.path. This is called an absolute import.
- 根据PEP-0328 关于 “Relative Imports” 的描述,考虑到相对路径的 import 在调整包的结构等方面的便利性,
Python 也支持相对路径的 import,并约定其语句格式为
from <> import。
The most important of which is being able to rearrange the structure of large packages without having to edit sub-packages. In addition, a module inside a package can’t easily import itself without relative imports.
import <>不能 import 模块内的属性,而from <> import可以
先来看看Python Reference 定义的 “The import statement”:
import_stmt ::= "import" module ["as" identifier] ("," module ["as" identifier])*
| "from" relative_module "import" identifier ["as" identifier]
("," identifier ["as" identifier])*
| "from" relative_module "import" "(" identifier ["as" identifier]
("," identifier ["as" identifier])* [","] ")"
| "from" relative_module "import" "*"
module ::= (identifier ".")* identifier
relative_module ::= "."* module | "."+
从以上描述可以发现,import 语句与 from import 语句在 import 对象上存在以下异同点:
- 相同点:两者都可以 import 模块(也可以 import 包,毕竟 package is also module.)
- 不同点:普通的 import 语句只能 import 模块,不能 import 模块中的属性; 而 from import 语句除了可以 import 模块,还可以 import 模块中的属性。
比如一个项目下有main.py和foo.py两个文件,并且foo.py定义了函数hi。
那么在main.py函数中,
import foo和from foo import hi是合法的import foo.hi是非法的,报错信息如下:ModuleNotFoundError: No module named 'foo.hi'; 'foo' is not a package
模块搜索机制
我从 Python Tutorial,Python Reference 和 realpython 网站上摘录了相关内容,如下所述。 综合这些内容,Python 的模块搜索机制可以概括如下:
- 首先查看
sys.modules,这个是 Python 对已加载模块的缓存。 - 然后查看 built-in 模块。
- 最后查看
sys.path,这个由三部分组成- 当前脚本所在路径
- 环境变量 PYTHONPATH 配置的路径
- 与Python 安装路径相关的一些路径(如 site-packages)
The Module Search Path
Python Tutorial “6. Modules”描述了 “The Module Search Path”:
When a module named spam is imported, the interpreter first searches for a built-in module with that name.
If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path.
sys.path is initialized from these locations:
- The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH(a list of directory names, with the same syntax as the shell variable PATH).- The installation-dependent default (by convention including a
site-packagesdirectory, handled by the site module).
from <> import 的搜索过程
Python Reference “7.11 The import statement”描述了 from <> import 的搜索过程:
The from form uses a slightly more complex process:
- find the module specified in the from clause, loading and initializing it if necessary;
- for each of the identifiers specified in the import clauses:
- check if the imported module has an attribute by that name
- if not, attempt to import a submodule with that name and then check the imported module again for that attribute
- if the attribute is not found, ImportError is raised.
- otherwise, a reference to that value is stored in the local namespace, using the name in the as clause if it is present, otherwise using the attribute name
Python import 的工作机制
realpython 的这篇博客也描述了Python import 的工作机制:
- 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.
模块加载机制
stack overflow 的这个回答 通俗易懂地解释了 Python 模块加载的工作机制:
-
running vs importing
- There is a big difference between directly running a Python file, and importing that file from somewhere else.
- Just knowing what directory a file is in does not determine what package Python thinks it is in.
- That depends, additionally, on how you load the file into Python (by running or by importing).
-
Two ways to load a Python file
- As the top-level script
- A file is loaded as the top-level script if you execute it directly, for instance by typing python myfile.py on the command line.
- There can only be one top-level script at a time; the top-level script is the Python file you ran to start things off.
- As a module
- A file is loaded as a module when an import statement is encountered inside some other file.
- As the top-level script
-
Naming
- When a file is loaded, it is given a name (which is stored in its
__name__attribute). - If it was loaded as the top-level script, its name is
__main__. - If it was loaded as a module, its name is the filename, preceded by the names of any packages/subpackages of which it is a part, separated by dots.
- If a module’s name has no dots, it is not considered to be part of a package.
- When a file is loaded, it is given a name (which is stored in its
-
Relative imports vs module name
- if your module’s name is
__main__, it is not considered to be in a package. - Its name has no dots, and therefore you cannot use
from .. importstatements inside it. - If you try to do so, you will get the “relative-import in non-package” error.
- if your module’s name is
-
Relative imports in interactive interpreter
- When you run the interactive interpreter, the “name” of that interactive session is always
__main__. - Thus you cannot do relative imports directly from an interactive session. Relative imports are only for use within module files.
- When you run the interactive interpreter, the “name” of that interactive session is always
Python 官方约定的 Import 风格
-
Imports should usually be on separate lines.
-
Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.
- Imports should be grouped in the following order:
- Standard library imports.
- Related third party imports.
- Local application/library specific imports.
- Imports should be grouped in the following order:
-
Absolute imports are recommended,
- as they are usually more readable and tend to be better behaved (or at least give better error messages) if the import system is incorrectly configured (such as when a directory inside a package ends up on sys.path).
- Explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose
-
When importing a class from a class-containing module, it’s usually okay to import the class name.
-
Wildcard imports (
from <module> import *) should be avoided.