python -m

python -m

在命令行中使用python时,cpython解析器会扫描命令行参数以及环境进行相应的操作和设置.输入文件名调用时,直接执行该脚本:

1
python myscript.py

使用-m module-name调用时,在sys.path 中搜索指定模块,并以__main__ 模块执行其内容。

1
python -m myscript

说明1

sys.path

python之所以能够导入一个模块(import module),是因为这个模块所在的目录在sys.path里,否则是无法进行导入的。在不同的虚拟环境中启动python时,他们的sys.path变量不同,因此在导入包时导入的就是相应环境中的包.

sys.path在lab环境下的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
['C:\\Users\\shen\\Lab',
'D:\\LAMMPS\\LAMMPS 64-bit 29Oct2020\\Python',
'D:\\Anaconda3\\envs\\lab\\python38.zip',
'D:\\Anaconda3\\envs\\lab\\DLLs',
'D:\\Anaconda3\\envs\\lab\\lib',
'D:\\Anaconda3\\envs\\lab',
'',
'D:\\Anaconda3\\envs\\lab\\lib\\site-packages',
'D:\\Anaconda3\\envs\\lab\\lib\\site-packages\\win32',
'D:\\Anaconda3\\envs\\lab\\lib\\site-packages\\win32\\lib',
'D:\\Anaconda3\\envs\\lab\\lib\\site-packages\\Pythonwin',
'D:\\Anaconda3\\envs\\lab\\lib\\site-packages\\IPython\\extensions',
'C:\\Users\\shen\\.ipython']

sys.path在base环境下的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
['C:\\Users\\shen\\Lab',
'D:\\LAMMPS\\LAMMPS 64-bit 29Oct2020\\Python',
'D:\\Anaconda3\\python38.zip',
'D:\\Anaconda3\\DLLs',
'D:\\Anaconda3\\lib',
'D:\\Anaconda3',
'',
'D:\\Anaconda3\\lib\\site-packages',
'D:\\Anaconda3\\lib\\site-packages\\win32',
'D:\\Anaconda3\\lib\\site-packages\\win32\\lib',
'D:\\Anaconda3\\lib\\site-packages\\Pythonwin',
'D:\\Anaconda3\\lib\\site-packages\\IPython\\extensions',
'C:\\Users\\shen\\.ipython']

因此想导入自己的模块时,最简单的方法就是把对应的python文件直接放到当前的工作目录下,工作目录就在sys.path中。sys也提供了一些其他接口,此外也可以使用PYTHONPATH环境变量来添加新目录:图片

PYTHONPATH环境变量始终放在sys.path的最上层

python 运行和 python -m 运行时sys.path的区别

新建G:.py文件如下:

1
2
3
import sys
if __name__ == '__main__':
print(sys.path)

分别用 pythonpython -m 运行:图片

直接启动时,解释器把py文件所在的目录添加到了sys.path中,而模块启动时,把输入命令所在的目录添加到了sys.path中

直接运行脚本必须给出脚本的完整路径,而模块启动时,解释器会自动在sys.path里寻找这个模块所在的位置,因此可以直接在命令行里调用python的某些模块,使用一些模块提供的功能(如果它提供了命令行接口),比如timeit模块:

图片

说明2

模块就是一个.py文件,而包是堆模块更高级的封装,python要求每一个包的目录下必须有一个__init__.py文件,python -m的参数应该是一个模块文件,如果后面的参数是一个包名称时,此时需要包的路径下有一个__main__.py文件,解释器把__main__.py文件作为主模块执行。新建G:,在该目录下新建空的__init__.py__main__.py文件,编辑__main__.py文件如下:

1
2
3
import sys
if __name__=='__main__':
print('我是一个包', sys.path, sep='\n')

python -m package运行结果如下:

说明3 - 包的导入

  1. 在导入一个包时,实际上是运行它的__init.py__文件,因此要想通过导入的包调用子模块时,需要在__init__.py文件中导入;导入一个模块时,实际上就是运行相应的.py文件。理解了这一点就理解了很多逻辑.

  2. __init__.py文件中的__all__变量:

    当不指明__all__变量时,运行from package import *时会导入package中的所有定义的符号,当指明__all__变量时,只有存在于__all__中的符号才会被导入:

    目录结构如下:

    package

    ├─__init__.py

    ├─subpackage_B

       ├─module_B.py
       └__init__.py

    ├─subpackage_A

       ├─module_A.py
       └__init__.py

    其中package下的__init__.py文件为:

    1
    from . import subpackage_A,subpackage_B

    subpackage_A下的__init__.py文件为:

    1
    from . import module_A

    subpackage_B下的__init__.py文件为:

    1
    from . import module_B

    module_A.py文件中定义了一个A变量,module_B.py文件中定义了一个B变量.

    若在package的__init__.py文件中不声明__all__变量,则subpackage_A和subpackage_B都会被导入:

图片

若定义__all__变量如下:

1
__all__ = ['subpackage_A']

则只有subpackage_A会被导入图片

  1. __name__变量

    __name__变量是一个模块的属性,当直接执行一个脚本的时候,这个脚本的__name__变量为__main__,当作为模块导入时,这个模块的__name__变量为它本身的名字.

  2. __package__变量

    __package__变量在PEP 366中引入,它是一种允许显式相对导入的机制。__package__变量是一个模块具有的属性。__package__变量有三种可能的值,对应三种可能的情况:

    (1) 一个包的名字:这个模块本身就是一个包(在python里包的类型也是module),如下面的package和package.subpackage_A,这时他们的__package__就是他们的__name__。当它不是一个包而是一个模块时(也就是一个.py文件时),他们的__package__名字是他们所在的包.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import package

    def func(module_or_package):
    print('__name__:', module_or_package.__name__)
    print('__package__:',module_or_package.__package__,'\n')

    func(package)
    func(package.subpackage_A)
    func(package.subpackage_A.module_A)

    输出为:

    1
    2
    3
    4
    5
    6
    7
    8
    __name__: package
    __package__: package

    __name__: package.subpackage_A
    __package__: package.subpackage_A

    __name__: package.subpackage_A.module_A
    __package__: package.subpackage_A

    (2) 一个空的字符串:一个单独的模块,不在某一个包里面,这个空字符串就代表顶级模块

    (3) None:如果用文件名直接运行这个模块(.py文件),它的__package__值为None

    考虑下面一个情形,目录结构如下:

    目录结构如下:

    package1

    ├─__init__.py

    ├─module.py

    package2

    ├─__init__.py

    ├─run.py

package1和package2在同一目录下,编辑run.py如下:

1
2
3
print('__name__:',__name__)
print('__package__:',__package__)
from package1 import module

运行:

1
python package2/run.py

输出为:

1
2
3
4
5
6
__name__: __main__
__package__: None
Traceback (most recent call last):
File "package2/run.py", line 3, in <module>
from package1 import module
ModuleNotFoundError: No module named 'package1'

因为此时run.py是一个文件,package1对于他来说是不可见的;

运行:

1
python -m package2.run

输出为:

1
2
__name__: __main__
__package__: package2

此时package2所在的目录在sys.path里,所以package1也是可见的,不会报错.

参考:

https://docs.python.org/zh-cn/3/using/cmdline.html

https://www.jianshu.com/p/04701cb81e38

https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p09_add_directories_to_sys_path.html

https://www.cnblogs.com/xueweihan/p/5118222.html

https://stackoverflow.com/questions/22241420/execution-of-python-code-with-m-option-or-not

https://www.jb51.net/article/174005.htm

https://stackoverflow.com/questions/21233229/whats-the-purpose-of-the-package-attribute-in-python

https://blog.csdn.net/weixin_38256474/article/details/81228492