温馨提示: 本篇演示环境是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++指针函数和函数指针概述

    欢迎指正 代码写的不够规范: 目的是缩短文章篇幅,实际中请注意 阅读完本文, 你一定能判断和写出:指针函数和函数指针. 0.结论 A.指针函数: 函数的返回值是指针类型 B.函数指针: 函数名是一个指 ...

  2. sigslot之简化版

    1.关于 1.1 基于原版sigslot做了部分修改.原版的信号支持最多支持8个参数,改进后,最多支持1个参数,这样肯定不能满足日常需求,但是,我们可以将信号的一个参数改为类型指针,比如信号定义时的模 ...

  3. 1135 - Count the Multiples of 3

    1135 - Count the Multiples of 3   PDF (English) Statistics Forum Time Limit: 3 second(s) Memory Limi ...

  4. idea使用教程-常用快捷键

    [1]创建内容:alt+insert [2]main方法:psvm [3]输出语句:sout [4]复制行:ctrl+d [5]删除行:ctrl+y [6]代码向上/下移动:Ctrl + Shift ...

  5. 使用.NET 6开发TodoList应用(10)——实现DELETE请求以及HTTP请求幂等性

    系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 先说明一下关于原本想要去更新的PATCH请求的文章,从目前试验的情况来看,如果是按照.NET 6的项目结构(即只使用一个Pro ...

  6. NFS 部署

    目录 NFS 部署 NFS简介 NFS应用 NFS工作流程图 NFS部署 服务端 客户端 测试NFS文件同步功能 NFS配置详解 NFS部分参数案例 统一用户 搭建考试系统 搭建步骤 配合NFS实现文 ...

  7. 标准基座获取定位可以获取address城市,自定义基座获取不到address

    正常的返回应该 { "type": "WGS84", "altitude": 0, "latitude": 31.830 ...

  8. 基于Spring MVC + Spring + MyBatis的【学生管理管理系统】

    资源下载:https://download.csdn.net/download/weixin_44893902/45602690 练习点设计:模糊查询.删除.新增 一.语言和环境 实现语言:JAVA语 ...

  9. 【MySQL作业】SELECT 数据查询——美和易思select 选择列表应用习题

    点击打开所使用到的数据库>>> 1.查询所有客户的地址和电话号码. SELECT address, phone FROM customer 2.查询所有商品的名称.种类和单价信息. ...

  10. PHP 中的多进程使用,进程通信、进程信号等详解

    多进程环境要求 Linux 系统 php-cli 模式 pcntl 扩展 或 swoole 扩展 pcntl 扩展 <?php $str = "hello world!" . ...