python relative import and python -m flag
absolute import
https://realpython.com/absolute-vs-relative-python-imports/#absolute-imports
从项目根目录开始, 使用绝对路径 查找 package 或者 module
An absolute import specifies the resource to be imported using its full path from the project’s root folder.
└── project ├── package1 │ ├── module1.py │ └── module2.py └── package2 ├── __init__.py ├── module3.py ├── module4.py └── subpackage1 └── module5.py
Let’s assume the following:
package1/module2.py
contains a function,function1
.package2/__init__.py
contains a class,class1
.package2/subpackage1/module5.py
contains a function,function2
.The following are practical examples of absolute imports:
from package1 import module1 from package1.module2 import function1 from package2 import class1 from package2.subpackage1.module5 import function2
优点: 明确,直接。
Pros and Cons of Absolute Imports
Absolute imports are preferred because they are quite clear and straightforward. It is easy to tell exactly where the imported resource is, just by looking at the statement. Additionally, absolute imports remain valid even if the current location of the import statement changes. In fact, PEP 8 explicitly recommends absolute imports.
缺点: 冗长。
Sometimes, however, absolute imports can get quite verbose, depending on the complexity of the directory structure. Imagine having a statement like this:
from package1.subpackage2.subpackage3.subpackage4.module5 import function6
That’s ridiculous, right? Luckily, relative imports are a good alternative in such cases!
relative import
https://realpython.com/absolute-vs-relative-python-imports/#relative-imports
A relative import specifies the resource to be imported relative to the current location—that is, the location where the import statement is. There are two types of relative imports: implicit and explicit. Implicit relative imports have been deprecated in Python 3, so I won’t be covering them here.
└── project ├── package1 │ ├── module1.py │ └── module2.py └── package2 ├── __init__.py ├── module3.py ├── module4.py └── subpackage1 └── module5.py
Recall the file contents:
package1/module2.py
contains a function,function1
.package2/__init__.py
contains a class,class1
.package2/subpackage1/module5.py
contains a function,function2
.You can import
function1
into thepackage1/module1.py
file this way:
# package1/module1.py from .module2 import function1 # package2/module3.py from . import class1 from .subpackage1.module5 import function2
优点:简洁
缺点:隐晦
Pros and Cons of Relative Imports
One clear advantage of relative imports is that they are quite succinct. Depending on the current location, they can turn the ridiculously long import statement you saw earlier to something as simple as this:
from ..subpackage4.module5 import function6
Unfortunately, relative imports can be messy, particularly for shared projects where directory structure is likely to change. Relative imports are also not as readable as absolute ones, and it’s not easy to tell the location of the imported resources.
相对导入不能在顶级模块中使用
https://zhuanlan.zhihu.com/p/145903888
现象
最常见的就是
ModuleNotFoundError: No module named 'moduleY'
、ValueError: attempted relative import beyond top-level package
、ModuleNotFoundError: No module named '__main__.moduleY'; '__main__' is not a package
等这些异常使用了相对导入的模块文件不能作为顶层执行文件
对于下面
moduleX.py
的代码,执行python moduleX.py
将会引发ModuleNotFoundError: No module named '__main__.moduleY'; '__main__' is not a package
异常。# moduleX.py from .moduleY import *
原理
相对寻址,使用__name__(模块全路径) 作为寻址依据。
而顶层模块,就是被python解析器直接运行的文件,
在此文件内 __name__ 被赋值为 __main__ 字符串,
此字符串并能作为全路径。
Relative imports use a module's
__name__
attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to'__main__'
) then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
实际上, 相对寻址, 还直接依赖另外一个魔法变量 __package__,
当__name__不为 __main__ 时候, 会构给 __package__ 赋值为 dirname(__name__)
否则, __package__ = None
实例
package/ __init__.py subpackage1/ __init__.py moduleX.py moduleY.py subpackage2/ __init__.py moduleZ.py moduleA.py
# moduleA.py print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__))) from subpackage2 import moduleZ
# moduleZ.py print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
python moduleA.py
__file__=D:/top/package/moduleA.py | __name__=__main__ | __package__=None __file__=D:\top\package\subpackage2\moduleZ.py | __name__=subpackage2.moduleZ | __package__=subpackage2
python -m 解决上面问题
https://zhuanlan.zhihu.com/p/91120727
-m mod run library module as a script (terminates option list)
python -m xx.yy
其中 xx.yy 为 import使用的模块全路径
这里寻址方式也跟import一致
如果yy对应文件 yy.py, 则直接执行此文件;
如果yy对应一个package, 则查找包下的 __main__.py 并执行。
对于上面两部的执行的文件, 在这两个文件中, 虽然 __name__ 都是 __main__ , 但是 __package__ 被赋值为 “” , 而不是 None, 这表示其支持了相对寻址方式。
"mod"是“module”的缩写,即“-m”选项后面的内容是 module(模块),其作用是把模块当成脚本来运行。
-m 选项的五个典型用法
Python 中有很多使用 -m 选项的场景,相信大家可能会用到或者看见过,我在这里想分享 5 个。
在 Python3 中,只需一行命令就能实现一个简单的 HTTP 服务:
python -m http.server 8000 # 注:在 Python2 中是这样 python -m SimpleHTTPServer 8000
已知一个模块的名字,但不知道它的文件路径,那么使用“-m”就意味着交给解释器自行查找,若找到,则当成脚本执行。
那么,“-m”方式与直接运行脚本相比,在实现上有什么不同呢?
- 直接运行脚本时,相当于给出了脚本的完整路径(不管是绝对路径还是相对路径),解释器根据文件系统的查找机制, 定位到该脚本,然后执行
- 使用“-m”方式时,解释器需要在不 import 的情况下,在所有模块命名空间 中查找,定位到脚本的路径,然后执行。为了实现这个过程,解释器会借助两个模块:
pkgutil
和runpy
,前者用来获取所有的模块列表,后者根据模块名来定位并执行脚本- 如果“-m”之后要执行的是一个包,那么解释器经过前面提到的查找过程,先定位到该包,然后会去执行它的“__main__”子模块,也就是说,在包目录下需要实现一个“__main__.py”文件。
实例
https://stackoverflow.com/questions/21233229/whats-the-purpose-of-the-package-attribute-in-python
All I want to know is what exactly does
__package__
meanIt is the mechanism that enables explicit relative imports.
There are three possible categories of values for
__package__
- A package name (a string)
- An empty string
- None
Package Name
That is, if a module is in a package,
__package__
is set to the package name to enable explicit relative imports. Specifically:When the module is a package, its
__package__
value should be set to its__name__
. When the module is not a package,__package__
should be set [...] for submodules, to the parent package’s name.Empty String
If a module is at root, or top-level, that is, the current module is imported with
import current_module
or when a top-level module is run as the entry point as with:
$ python -m current_module
then
__package__
is an empty string. Or as the documentation says:When the module is not a package,
__package__
should be set to the empty string for top-level modules...None
If a module/script is run by filename,
__package__
is None:When the main module is specified by its filename, then the
__package__
attribute will be set to None.Evidence
First, let's create a file structure with noisy debugging - using Python 3.6:
text = "print(f'{__name__}, __file__: {__file__}, __package__: {repr(__package__)}')" from pathlib import Path Path('foo.py').write_text(text) Path('package').mkdir() Path('package/__init__.py').write_text(text) Path('package/__main__.py').write_text(text) Path('package/bar.py').write_text(text) # and include a submodule with a relative import: Path('package/baz.py').write_text(text + '\nfrom . import bar')
Now we see that foo.py executed as a module has an empty string for
__package__
, while the script executed by file name as the entry point hasNone
:
$ python -m foo __main__, __file__: ~\foo.py, __package__: '' $ python foo.py __main__, __file__: foo.py, __package__: None
When we execute a package as a module for the entry point, its
__init__.py
module runs, then its__main__.py
runs:
$ python -m package package, __file__: ~\package\__init__.py, __package__: 'package' __main__, __file__: ~\package\__main__.py, __package__: 'package'
Similarly, when we execute a submodule as a module for the entry point, the
__init__.py
module runs, then it runs:
$ python -m package.bar package, __file__: ~\package\__init__.py, __package__: 'package' __main__, __file__: ~\package\bar.py, __package__: 'package'
Finally, we see that the explicit relative import, the entire reason for having
__package__
, (which happens last here) is enabled:
$ python -m package.baz package, __file__: ~\package\__init__.py, __package__: 'package' __main__, __file__: ~\package\baz.py, __package__: 'package' package.bar, __file__: ~\package\bar.py, __package__: 'package'
Note, in the output, I have substituted
~
for the parent directories.
python -m pip install 必要性
https://snarky.ca/why-you-should-use-python-m-pip/
当有多个python版本安装在同一台环境上, 那么 pip install是对哪一个 python版本生效不得而知。
如果使用 python -m pip install 则可以指定python版本。
What is
python -m pip
?To begin with,
python -m pip
executespip
using the Python interpreter you specified aspython
. So/usr/bin/python3.7 -m pip
means you are executingpip
for your interpreter located at/usr/bin/python3.7
. You can read the docs on-m
if you're unfamiliar with the flag and how it works (it's very handy).
Why use
python -m pip
overpip
/pip3
?But when you use
python -m pip
withpython
being the specific interpreter you want to use, all of the above ambiguity is gone. If I saypython3.8 -m pip
then I know pip will be using and installing for my Python 3.8 interpreter (same goes for if I had saidpython3.7
).