Too young, too simple. Sometimes, naive & stupid

变量(Python)

增加一个变量

Python脚本的运行并非天马行空,它取决于不同的平台和启动方式,Python程序可能有各种特定的环境,即程序启动时,操作系统会自动传给程序的信息。比如,脚本可以获取如下几类系统级的输入和接口:

当前工作路径(CWD)

os.getcwd可以获取脚本启动目录,许多文件工具隐式地使用该变量。

命令行参数

sys.argv可以获取在命令行键入的启动参数,将其作为脚本的输入。

shell变量

os.environ可以获取运行它的shell(或父程序)中命名的变量,并传给脚本。

标准流

sys.stdinsys.stdoutsys.stderr是三个核心的命令行shell工具,负责输入/输出流,他们可以被脚本以如下方式使用:print,os.popen调用以及subprocess模块,io.StringIOl类等。

当前工作路径

当前工作路径(CWD)在脚本的执行中是一个重要概念。除非指定绝对路径,否则当脚本处理文件时将始终默认他们存在于CWD。脚本可以使用os.getcwd获取明确的CWD路径,使用os.chdir可以改变它的CWD。

谨记,没有完整目录路径的文件名将被映射到CWD路径,和你的PYTHONPATH设置无关。从技术上讲,一个脚本总是启动于CWD,而非它所在的目录。反之,import永远首先先搜索文件所在目录,而非CWD (除非该脚本刚好在CWD目录)。

CWD、文件和import路径

当你输入一个shell命令行,比python dir1\dir2\file.py 来运行该脚本时,CWD是你键入该命令时所处的路径,而非dir1\dir2。另一方面,Python自动将脚本所在的目录添加到模块搜索路径的最前面,因而无论如何从哪里运行,file.py总是可以导入dir1\dir2中的其他文件。我们写一个简单的脚本,打印出它的CWD和模块搜索路径来说明:

1
2
3
4
import os, sys
print('my os.getcwd =>', os.getcwd()) # 输出cwd执行命令
print('my sys.path =>', os.path[:6]) # 输出前六个导入路径
input() # 等待输入

现在,在该脚本所在路径运行它,CWD会如预期所设,同时该路径将会加到模块搜索路径的最前面。当在交互模式下运行时,它的第一个实体可能是一个空字符串:

但是如果我们早别的路径下运行该脚本,CWD将会随之而变(即我们输入命令的路径),并且Python会把脚本所在路径添加到木块搜索路径最前面,从而使得脚本能够看到它所在的目录的所有文件。

实际效果是,脚本没有目录路径的文件名将被映射到输入命令行的地方(os.getcwd),而通过sys.path列表中的首项,import总是可以看到当前正在运行脚本的路径,最后,当通过单机图标执行一个脚本时,CWD将被设置为该文件的所在路径。

在这种情况下,文件名所用的CWD以及第一个模块搜索路径都将是该脚本所在路径。有一下两个陷阱需要避免:

  • 如果不确定脚本的执行路径,最好使用完整的目录路径限定文件名。
  • 命令行脚本不能总是依赖于CWD来导入不在它所处目录的文件,而应使用PYTHONPATH的设置和包导入路径来访问其他目录中的模块。

命令行参数

Python可以通过sys模块获取脚本启动时命令行的输入信息,通常,他们被称为命令行参数,以内置字符串列表的形式存在于sys.argv中,C语言的程序员可能注意到它和C的argv数组类似,交互命令行方式启动,的Python没有命令行参数被传入,所以argv将是空串。

1
2
3
>>> import sys
>>> sys.argv
['']

如同函数参数的作用,命令行参数扮演着同样的角色,他们都为程序传递可变信息。由于不需要编码,他们时脚本更通用,比如,一个人间处理脚本可以使用一个命令行参数传递要处理的文件名,还可以处理模式的flag、Internet地址等。

解析命令行参数

一旦使用命令行参数,你会发现直接操作参数列表是不容易的。更具代表性的是,程序在启动的时候将它们转为更易用的结构。举例说明,这个脚本是扫描argv列表查找-optionname optionvalue值对,并以optionname为键值保存到字典结构中方便使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"collect command-line options in a directionary"
def getopts(argv):
opts = {}
while argv:
if argv[0][0] == '-' # 找到 “-名称 值” 键值对
opts[argv[0]] = argv[1] # 字典关键字为参数 “-名称”
argv = argv[2:]
else:
argv = argv[1:]
return opts
if __name__ == '__main__':
from sys import argv
myargs = getopts(argv)
if '-i' in myargs:
print(myargs['-i'])
print(myargs)

你可以在你的命令行工具中导入并使用该函数,在独立运行时,它将打印如下格式化的参数字典:

1
2
3
4
5
python testargv.py
{}

python testargv.py -i data.txt -o results.txt data.txt
{'-o': 'results.txt', '-i': 'data.txt'}

自然地,我们还可以为它增加参数模式、错误检查等复杂功能。对于更复杂的命令行,我们可以使用Python标准库中的命令行处理工具来解析:

  • getopt模块,仿效Unix/C中的同名工具。
  • optparse模块,一个新的替代只选,通常认为它的功能更强大。

Python库手册中对二者都有介绍,同时提供了使用示例,通常,脚本可配置性越高,命令行处理逻辑的复杂度就越高。