温馨提示: 本篇演示环境是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++内存分布之虚函数(多继承)

    系列 c++内存分布之虚函数(单一继承) c++内存分布之虚函数(多继承) [本文] 结论 1.虚函数表指针 和 虚函数表 1.1 影响虚函数表指针个数的因素只和派生类的父类个数有关.多一个父类,派生 ...

  2. c/c++11封装UDP,支持ipv4和ipv6,支持接收和发送

    更新日志 11/06/2021 1.增加IPV6 2.ipv6通过windows10初步测试 3.ipv6包括: 接收和发送 5.增加错误代码接口 6.本机IPv6截图 7.编译通过截图 8.ipv6 ...

  3. 【LeetCode】代码模板,刷题必会

    目录 二分查找 排序的写法 BFS的写法 DFS的写法 回溯法 树 递归 迭代 前序遍历 中序遍历 后序遍历 构建完全二叉树 并查集 前缀树 图遍历 Dijkstra算法 Floyd-Warshall ...

  4. python学习第三天:python基础(数据类型和变量)

    注释 以  # 开头的语句是注释,如,注释不会被编译运行: 格式 当语句以冒号:结尾时,缩进的语句视为代码块.按照约定俗成的管理,应该始终坚持使用4个空格的缩进(在文本编辑器中,需要设置把Tab自动转 ...

  5. Augmentation For GAN

    目录 概 主要内容 Differentiable Augmentation Adaptive Augmentation 代码 Zhao S., Liu Z., Lin J., Zhu J. and H ...

  6. Spring企业级程序设计作业目录(作业笔记)

    Spring企业级程序设计 • [目录] 第1章 Spring之旅  >>> 1.1.6 使用Eclipse搭建的Spring开发环境,使用set注入方式为Bean对象注入属性值并打 ...

  7. 编写Java程序,比较两个Dog对象是否为同一个对象

    返回本章节 返回作业目录 需求说明: 重写Dog类的equals(Object obj)方法. 如果equals(Object obj)中obj为Dog类型,则判断当前 对象的dogName与obj对 ...

  8. netty系列之:netty对SOCKS协议的支持

    目录 简介 SocksMessage Socks4Message Socks5Message 总结 简介 SOCKS是一个优秀的网络协议,主要被用来做代理,它的两个主要版本是SOCKS4和SOCKS5 ...

  9. nginx worker_cpu_affinity使用方法

    Nginx默认没有开启利用多核CPU,我们可以通过增加worker_cpu_affinity配置参数来充分利用多核CPU.CPU是任务处理,计算最关键的资源,CPU核越多,性能就越好. 配置Nginx ...

  10. CF858D Polycarp's phone book

    题意翻译 有 n 个长度为 9 且只包含数字字符互不相同的串. 需要对于每个串找到一个长度最短的识别码,使得这个识别码当且仅当为这个串的子串. 题目分析 因为范围不是非常大,所以可以将子串筛出来 然后 ...