python中sys模块必备知识

官方解释:https://docs.python.org/3/library/sys.html

顾名思义,sys模块是python中比较底层的库,它提供了一系列与python解释器交互的接口。在大多数情况下我们并不会显式的用到这个模块,但是实际上每个程序的运行都离不开这个模块,了解这个模块做了什么有助于我们理解代码的运行逻辑和规避掉一些潜在的问题。本文将就sys模块几个常用接口的用法做详细介绍。

必备知识——sys.path

在写python项目时,通常会涉及到模块之间的导入,对于比较大的项目来说,经常出现找不到要导入的模块或者导入的方法错误之类的问题。要解决这些问题其实都离不开sys.path,个人认为,每一个使用python的人都应该对sys.path有清晰的理解。

首先从一个问题开始,现在可以想一想,下边这句话python解释器是怎么找到file_b.py的呢?

1
from file_b import function_1

实际上这就是sys.path的作用所在,它是一个指定了模块搜索路径的字符串列表(没错,它不是什么高深莫测的东西,仅仅是一个列表),列表中的每一个字符串都是一个路径,这些路径告诉解释器import的模块要去哪里找,默认是环境变量中的路径。实际使用中我们可以根据项目修改其内容以指定要搜索的路径。

sys.path在程序启动时被初始化, 简单理解的话可以认为此时的sys.path包含两部分内容,第一部分是调用解释器的脚本的路径,运行哪个脚本就把哪个脚本的目录插入sys.path的开头;第二部分是环境变量中的路径。

第二部分没什么好说的,这里举例论证第一部分的路径。如我在sys_module_demo目录下创建模块file1.py,内容为:

1
2
import sys
print('sys.path[0] ==> ', sys.path[0])

保存后运行以下命令:

1
python sys_module_demo/file1.py

那么程序将打印出sys_module_demo目录的绝对路径,比如我的输出是sys.path[0] ==> /home/ven/sys_module_demo。这个路径就是第一部分的路径,其被添加到sys.path中后,同在这个路径下的模块就也被解释器“找到”了。

一个例子

上边我们以单个文件的运行举例,实际上在这种情况下,我们完全不必关注sys.path,因为不涉及到自定义模块之间的相互调用。sys.path最大的作用体现在处理多个处于不同路径下的模块之间的调用问题上,下边将举例说明。

假如说存在如下项目路径:

1
2
3
4
project
├──workwpace
│ └──file1.py
└──file2.py

如果要在file1.py中导入file2.py应该怎么办呢?换句话说file1.py怎么才能知道去哪里找到file2.py呢?

这时候就需要请sys.path出场了,只需要手动地将file2.py的目录加进sys.path中就万事大吉了,两个文件的内容分别如下:

file1.py内容:

1
2
3
4
5
6
7
import sys
sys.path.append('/home/ven/project')
# or relative path
# sys.path.append('../')
import file2

print('file1 invoked!')

file2.py内容:

1
print('file2 invoked!')

此时运行file1.py程序将正常运行并输出:

1
2
file2 invoked!
file1 invoked!

然而...

在大型的项目中使用sys.path有用且必要,但是频繁地在sys.path中添加路径并不是个好习惯,最好的方法是只把项目的根路径添加到sys.path中。

进阶知识

sys还提供了一系列与系统交互的功能,在面对一些特定需求时这些功能将非常有用,这里简单记录两个功能。

获取python版本——sys.version

如果不确定程序要运行在哪个版本的python上,而又想用某些版本特有的语法,那么这个接口就是个非常不错的工具,我们可以尝试一下:

1
2
>>> sys.version
'3.6.2 (v3.6.2:5fd33b5, Jul 8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]'

可见我的python版本是3.6。

然而在实际使用中我们更经常用另一个接口sys.version_info来获取版本信息,至于原因我们用一下就知道了:

1
2
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=2, releaselevel='final', serial=0)

sys.version_info把版本信息更精细的整理出来了,而不是像sys.version那样单纯甩给我们一个字符串,这样的输出在程序中非常友好。

假如说我在python2中开发,习惯于将print作为关键字使用,可是如果别人拿走我的程序在python3中运行那不就报错了吗?解决方法就是用sys.version_info

1
2
3
4
if sys.version_info <= (3,0):
print 'this is python3!'
else:
print('this is python2!')

处理命令行参数——sys.argv

在这一点上sys.argv类似于shell编程中的命令行参数,即: sys.argv[0]是运行的文件名本身 sys.argv[1]是第一个参数 sys.argv[2]是第二个参数 以此类推。需要注意的是每一个参数都会被当作字符串

建立一个文件file.py,内容为:

1
2
3
4
5
6
7
8
import sys
print('file name: ', sys.argv[0])
print('first argument: ', sys.argv[1])
print('second argument: ', sys.argv[2])

# string -> int
a, b = int(sys.argv[1]), int(sys.argv[2])
print('sum of arguments: ', a + b)

如下运行file.py

1
python file.py 2 3

将会发现输出结果为:

1
2
3
4
file name: file.py
first argument: 2
second argument: 3
sum of arguments: 5

实际上,这个功能使用的并不多,因为有更好的替代方案argparser。不过,在随手写的小程序中,sys.argv不失为一个小巧而高效的工具。