温馨提示: 本篇演示环境是Python 3.8

python --help看下python -m参数的解释:

-m mod : run library module as a script (terminates option list)

modmodule的缩写,即-m后面跟的是模块(module)名,意思是把模块当成脚本来运行。

terminates option list意味着-m之后的其它选项不起作用,在这点上它跟-c是类似,都是终极选项。

既然涉及到模块,这里就多提几句。 在Python中,一个.py文件就称之为一个模块(Module)。

比如一个顶层包名bytesfly,按照如下目录存放:

bytesfly
├─ __init__.py
├─ __main__.py
└─ fly.py

上面fly.py模块的名字就是bytesfly.fly

注意: 模块名是不带.py后缀的。

关于模块更详细的讲解见之前的博客:

https://www.cnblogs.com/bytesfly/p/python.html#模块

python -m 常见用法

  • 使用cProfile模块分析程序函数调用链耗时
python -m cProfile -s cumulative bytesfly/fly.py
  • 使用pdb模块以调试模式来执行Python脚本
python -m pdb bytesfly/fly.py
  • 使用http.server模块实现一个简单的HTTP服务
python -m http.server 9000
  • 使用pydoc模块生成HTML格式的官方帮助文档,可以在浏览器中访问
python -m pydoc -p 9001
  • python -m pip install xxx

在存在多个Python版本的环境中,这种写法可以精确地控制三方库的安装位置。例如用python3.8 -m pip,可以明确指定给3.8版本安装,而不会混淆成其它的版本。

当然现在我们大多使用conda之类的虚拟环境管理器和包管理器,可能不会出现上面所说的这种混淆情况。这里只是提一下。

  • 使用timeit模块分析执行耗时
python -m timeit -n 3 -r 2 "import time;time.sleep(1)"

其实调用的是:

timeit.repeat("import time;time.sleep(1)", repeat=2, number=3)

看一眼timeit.py中的源码就能快速理解-n -r参数的意思:

def repeat(self, repeat=default_repeat, number=default_number):
"""Call timeit() a few times. This is a convenience function that calls the timeit()
repeatedly, returning a list of results. The first argument
specifies how many times to call timeit(), defaulting to 5;
the second argument specifies the timer argument, defaulting
to one million.
"""
r = []
for i in range(repeat):
t = self.timeit(number)
r.append(t)
return r

-p/--process: use time.process_time() (default is time.perf_counter())

timeit后面还能添加-p参数,如下:

python -m timeit -p -n 3 -r 2 "import time;time.sleep(1)"

其实调用的是:

timeit.repeat("import time;time.sleep(1)", repeat=2, number=3, timer=time.process_time)

再看一眼time.process_time()的代码注释

def process_time(): # real signature unknown; restored from __doc__
"""
process_time() -> float Process time for profiling: sum of the kernel and user-space CPU time.
"""
return 0.0

加上了-p参数计算的是sum of the kernel and user-space CPU time,讲白了就是程序占用CPU的时间,程序睡眠或者请求网络IO阻塞的时间不算。

python -m 原理解析

看了上面python -m几种常见用法,你是否好奇python -m到底做了什么事?

不卖关子,一句话解释就是:

对于python -m module_name,Python会检索sys.path,查找名字为module_name的模块或者包,并将其内容当成主程序入口来执行,换句话说在执行时,该脚本的__name____main__

拿文章开篇的bytesfly.fly模块来说,也就是bytesfly包下的fly.py文件内容如下:

import sys

print("----fly----")

if __name__ == '__main__':
print("fly_main")
print(sys.path)

hello项目路径下执行python -m bytesfly.fly,输出如下:

----fly----
fly_main
['/home/bytesfly/py/hello', '/home/bytesfly/anaconda3/envs/test/lib/python38.zip']

如果直接执行呢? 即相同路径下执行python bytesfly/fly.py,输出如下:

----fly----
fly_main
['/home/bytesfly/py/hello/bytesfly', '/home/bytesfly/anaconda3/envs/test/lib/python38.zip']

总结一下,python -m module_namepython folder/file.py,都会把定位到的Python脚本当成主程序入口来执行,即在执行时,该脚本的__name__都是__main__,与import导入模块不同。

但是有注意到上面两种调用方式的不同之处吗?

fly.py程序输出了sys.path,可以看到两种调用方式的Python Path有区别,这种区别有什么影响呢?

再看一个例子。 比如一个顶层包名还是bytesfly,按照如下目录存放:

bytesfly
├─ __init__.py
├─ __main__.py
└─ fly.py
└─ a
├─ __init__.py
└─ run.py
└─ b
├─ __init__.py
└─ tool.py

其中tool.py内容如下:

def add(a, b):
return a + b

其中run.py内容如下:

import sys

print(sys.path)

if __name__ == '__main__':
from bytesfly.b import tool print(tool.add(1, 2))

同样在hello项目路径下执行python -m bytesfly.a.run,输出如下:

['/home/bytesfly/py/hello', '/home/bytesfly/anaconda3/envs/test/lib/python38.zip']
3

然后在hello项目路径下执行python bytesfly/a/run.py,输出如下:

['/home/bytesfly/py/hello/bytesfly/a', '/home/bytesfly/anaconda3/envs/test/lib/python38.zip']
Traceback (most recent call last):
File "bytesfly/a/run.py", line 6, in <module>
from bytesfly.b import tool
ModuleNotFoundError: No module named 'bytesfly'

这个地方能Get到这两种调用方式在Python Path上的区别?

除此之外,python -m module_namepython folder/file.py,在实现上有什么不同呢?

  • 使用python -m module_name,解释器在不import模块的情况下,在所有模块命名空间中查找,定位到脚本的路径,然后执行。为了实现这个过程,解释器会借助两个模块:pkgutilrunpy,前者用于获取所有的模块列表,后者根据模块名来定位并执行脚本
  • 直接运行脚本时,相当于给出了脚本的完整路径(不管是绝对路径还是相对路径),解释器根据文件系统的查找机制,定位到该脚本,然后执行

python -m 补充说明

python -m module_name这里的module_name也可以是包名。

还是用上面的顶层包名bytesfly举例,按照如下目录存放:

bytesfly
├─ __init__.py
├─ __main__.py
└─ fly.py
└─ a
├─ __init__.py
└─ run.py
└─ b
├─ __init__.py
└─ tool.py

其中__main__.py内容如下:

import sys

print("---bytesfly---")

if __name__ == '__main__':
print(sys.path)

项目路径下执行python -m bytesfly,输出如下:

---bytesfly---
['/home/bytesfly/py/hello', '/home/bytesfly/anaconda3/envs/test/lib/python38.zip']

如果执行python -m bytesfly.a,输出如下:

No module named bytesfly.a.__main__; 'bytesfly.a' is a package and cannot be directly executed

原来,python -m bytesfly等效于python -m bytesfly.__main__

写在最后

有了python -m module_name选项,在命令行中使用内置模块、标准包与第三方模块更加方便。

参考:

https://www.cnblogs.com/pythonista/p/11829632.html

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

python -m详解的更多相关文章

  1. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  2. [转] Python Traceback详解

    追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a   Python Traceback详解   刚接触Python的时候,简单的 ...

  3. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

  4. Python 递归函数 详解

    Python 递归函数 详解   在函数内调用当前函数本身的函数就是递归函数   下面是一个递归函数的实例: 第一次接触递归函数的人,都会被它调用本身而搞得晕头转向,而且看上面的函数调用,得到的结果会 ...

  5. python线程详解

    #线程状态 #线程同步(锁)#多线程的优势在于可以同时运行多个任务,至少感觉起来是这样,但是当线程需要共享数据时,可能存在数据不同步的问题. #threading模块#常用方法:'''threadin ...

  6. python数据类型详解(全面)

    python数据类型详解 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8.字典9.日期 1.字符串1.1.如何在Python中使用字符串a.使用单引号(')用单引号括起来表示字 ...

  7. Python Collections详解

    Python Collections详解 collections模块在内置数据结构(list.tuple.dict.set)的基础上,提供了几个额外的数据结构:ChainMap.Counter.deq ...

  8. python生成器详解

    1. 生成器 利用迭代器(迭代器详解python迭代器详解),我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成.但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记 ...

  9. 转 python数据类型详解

    python数据类型详解 目录 1.字符串 2.布尔类型 3.整数 4.浮点数 5.数字 6.列表 7.元组 8.字典 9.日期 1.字符串 1.1.如何在Python中使用字符串 a.使用单引号(' ...

  10. python多线程详解

    目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 二.线程实现 threading模块 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 互斥锁 递归锁 信号量 ...

随机推荐

  1. C++基础之自增和自减运算符的重载

    1. 格式 1.1 分为前置和后置格式: int x = 0; int y = 0; // 后置自增运算符 x++; // 前置自增运算符 ++x; // 后置自减运算符 y--; // 前置自减运算 ...

  2. 【LeetCode】232. Implement Queue using Stacks 解题报告(Python & Java)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Python解法 Java解法 日期 [LeetCo ...

  3. 【LeetCode】495. Teemo Attacking 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  4. LeetCode 第三大的数414. Third Maximum Number

    题目 描述:给定数组中求第三大的数字:如果没有,返回最大的:时间复杂度O(n) 记得<剑指offer>才看到过这样的求第k大的题目.但是忘记具体怎么做了.只好先自己想了. 因为时间复杂度的 ...

  5. 最大流问题的Ford-Fulkerson模板

    详细讲解:http://blog.csdn.net/smartxxyx/article/details/9293665 下面贴上我的第一道最大流的题: hdu3549 1 #include<st ...

  6. ROC and AUC

    目录 概 TPR, FPR ROC and AUC 代码 ROC-wiki 概 AUC常常在文章中作为评价一个分类器优劣的指标, 却总是忘记其原由, 索性记上一笔. TPR, FPR 首先理解TP, ...

  7. Defending Adversarial Attacks by Correcting logits

    目录 概 主要内容 实验 Li Y., Xie L., Zhang Y., Zhang R., Wang Y., Tian Q., Defending Adversarial Attacks by C ...

  8. Java高级程序设计笔记 • 【第1章 IO流】

    全部章节   >>>> 本章目录 1.1 File类访问文件 1.1.1 File 类 1.1.2 File 类方法 1.1.3 实践练习 1.2 文件过滤器 1.2.1 Fi ...

  9. vue grammer one

    本文所有内容均来自 书籍<vue.js实战> 完整代码请查看github v-model <!DOCTYPE html> <html> <head> & ...

  10. 每天学一点——python用户的交互、格式化输出与基础运算符运用

    用户交互 input输入 input接收的数据都是字符串类型 如下图 output输出 还可以相加 换行符 想让他们隔行排列的话就可以这样(如图) 在想各行的开头前面加上\n即可 那若是想将两个输出的 ...