Python 核心编程(第二版)——执行环境
许多的python 对象都是我们所说的可调用的,即是任何能通过函数操作符“()”来调用的对象。要调用可调用对象,函数操作符得紧跟在可调用对象之后。可调用对象可以通过函数式编程接口来进行调用,如apply(),filter(),map(),以及reduce()。Python 有4 种可调用对象:函数,方法,类,以及一些类的实例。记住这些对象的任何引用或者别名都是可调用的。
函数:python 有3 种不同类型函数对象。第一种是内建函数。
内建函数属性 | |
BIF属性 | 描述 |
bif.__doc__ | 文档字符串(或None) |
bif.__name__ | 字符串类型的文档名字 |
bif.__self__ | 设置为None(保留给built-in 方法) |
bif.__module__ | 存放bif 定义的模块名字(或None) |
BIF 有基础类型属性,可以用dir()列出函数的所有属性。
UDF(User-Defined Function,用户定义的函数)通常是用python 写的,定义在模块的最高级,因此会作为全局名字空间的一部分(一旦创建好内建名字空间)装载到系统中。函数也可在其他的函数体内定义,并且由于在2.2 中嵌套作用域的改进,我们现在可以对多重嵌套作用域中的属性进行访问。可以用func_closure 属性来钩住在其他地方定义的属性。
用户自定义函数属性 | |
UDF属性 | 描述 |
udf.__doc__ | 文档字符串(也可以用udf.func_doc) |
udf.__name__ | 字符串类型的函数名字(也可以用 udf.func_name) |
udf.func_code | 字节编译的代码对象 |
udf.func_defaults | 默认的参数元组 |
udf.func_globals | 全局名字空间字典; 和从函数内部调用globals(x)一样 |
udf.func_dict | 函数属性的名字空间 |
udf.func_doc | (见上面的udf.__doc__) |
udf.func_name | (见上面的udf.__name__) |
udf.func_closure | 包含了自由变量的引用的单元对象元组(自用变量在UDF 中使用,但在别处定义;参见python[语言]参考手册) |
lambda 表达式和用户自定义对函数相比,略有不同。虽然它们也是返回一个函数对象,但是lambda 表达式不是用def 语句创建的,而是用lambda 关键字:因为lambda 表达式没有给命名绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,然后就可以直接调用或者再通过函数来调用。变量仅是个别名,并不是函数对象的名字。通过lambda 来创建函数的对象除了没有命名之外,享有和用户自定义函数相同的属性;__name__或者func_name 属性给定为字符串"<lambda>"。
内建方法(BIM)属性 | |
BIM属性 | 描述 |
bim.__doc__ | 文档字串 |
bim.__name__ | 字符串类型的函数名字 |
bim.__self__ | 绑定的对象 |
只有内建类型(BIT)有BIM。BIM 和BIF 两者也都享有相同属性。不同之处在于BIM 的__self__属性指向一个Python对象,而BIF 指向None。对于类和实例,都能以该对象为参数,通过内建函数dir()来获得他们的数据和方法属性。
UDM(User-defined method,用户定义的方法)包含在类定义之中,只是拥有标准函数的包装,仅有定义它们的类可以使用。如果没有在子类定义中被覆盖掉,也可以通过子类实例来调用它们。
访问对象本身将会揭示你正在引用一个绑定方法还是非绑定方法。
用户自定义属性 | |
UDM属性 | 描述 |
udm.__doc__ | 文档字符串(与udm.im_fuc.__doc__相同) |
udm.__name__ | 字符串类型的方法名字(与umd.im_func.__name__相同) |
udm.__module__ | 定义udm 的模块的名字(或none) |
udm.im_class | 方法相关联的类(对于绑定的方法;如果是非绑定,那么为要求udm 的类) |
udm.im_func | 方法的函数对象(见UDFs) |
udm.im_self | 如果绑定的话为相关联的实例,如果非绑定位为none |
类:可利用类的可调用性来创建实例。“调用”类的结果便是创建了实例,即大家所知道的实例化。类有默认构造函数,该函数什么都不做,基本上只有一个pass 语句。程序员可以通过实现__int__()方法,来自定义实例化过程。实例化调用的任何参数都会传入到构造函数里。
python 给类提供了名为__call__的特别方法,该方法允许程序员创建可调用的对象(实例)。默认情况下,__call__()方法是没有实现的,这意味着大多数实例都是不可调用的。然而,如果在类定义中覆盖了这个方法,那么这个类的实例就成为可调用的了。调用这样的实例对象等同于调用__call__()方法。自然地,任何在实例调用中给出的参数都会被传入到__call()__中。那么foo()就和foo.__call__(foo)的效果相同, 这里foo 也作为参数出现,因为是对自己的引用,实例将自动成为每次方法调用的第一个参数。如果 ___call___()有参数,比如,(self, arg),那么foo(arg)就和调用foo.__call__(foo, arg)一样。
记住只有定义类的时候实现了__call__方法,类的实例才能成为可调用的。
2. 代码对象
每个可调用物的核心都是代码对象,由语句,赋值,表达式,以及其他可调用物组成。察看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码可以分成语句,赋值,表达式,以及可调用物。一般说来,代码对象可以作为函数或者方法调用的一部分来执行,也可用exec 语句或内建函数eval()来执行。从整体上看,一个python 模块的代码对象是构成该模块的全部代码。
3. 可执行的对象声明和内建函数
Python 提供了大量的BIF 来支持可调用/可执行对象,其中包括exec 语句。这些函数帮助程序员执行代码对象,也可以用内建函数complie()来生成代码对象。
可执行对象和内建函数 | |
内建函数和语句 | 描述 |
callable(obj) | 如果obj 可调用,返回True,否则返回FALSE |
compile(string,file, type) | 从type 类型中创建代码对象;file 是代码存放的地方(通常设为"") |
eval(obj, glo-bals=globals() ,locals=locals()) |
对obj 进行求值,obj 是已编译为代码对象的表达式,或是一个字符串表达式;可以给出全局或者/和局部的名字空间 |
exec obj | 执行obj、单一的python 语句或者语句的集合,也就是说格式是代码对象或者字符串;obj 也可以是一个文件对象(已经打开的有效python 脚本中) |
input(prompt='') | 等同于eval(raw_input(prompt=”)) |
callable()是一个布尔函数,确定一个对象是否可以通过函数操作符(())来调用。如果函数可调用便返回True,否则便是False。
compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用exec 语句或者内建函数eval()来执行这些对象或者对它们进行求值。一个很重要的观点是:exec 和eval()都可以执行字符串格式的Python 代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile()函数提供了一次性字节代码预编译,以后每次调用的时候,都不用编译了。
compile 的三个参数都是必需的,第一参数代表了要编译的python 代码。第二个字符串,虽然是必需的,但通常被置为空串。该参数代表了存放代码对象的文件的名字(字符串类型)。compile 的通常用法是动态生成字符串形式的Python 代码, 然后生成一个代码对象——代码显然没有存放在任何文件。最后的参数是个字符串,它用来表明代码对象的类型。有三个可能值:
'eval' 可求值的表达式[和eval()一起使用] |
'single' 单一可执行语句[和exec 一起使用] |
'exec' 可执行语句组[和exec 一起使用] |
eval()对表达式求值,后者可以为字符串或内建函数complie()创建的预编译代码对象。这是eval()第一个也是最重要的参数.......这便是你想要执行的对象。第二个和第三个参数,都为可选的,分别代表了全局和局部名字空间中的对象。如果给出这两个参数,globals 必须是个字典,locals可以是任意的映射对象,如果都没给出这两个参数,分别默认为globals()和locals()返回的对象,如果只传入了一个全局字典,那么该字典也作为locals 传入。
exec 语句执行代码对象或字符串形式的python 代码。exec 语句只接受一个参数,如:exec obj。被执行的对象(obj)可以只是原始的字符串。
内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input())。input()有一个可选的参数,该参数代表了给用户的字符串提示。如果不给定参数的话,该字符串默认为空串。input()返回的数据是对输入表达式求值的结果:一个python 对象。
exit()的任何整数参数作为退出状态会返回给调用者,该值默认为0。
4. 执行其他(Python)程序
在运行时刻,有很多执行另外python 脚本的方法。第一次导入模块会执行模块最高级的代码。不管你是否需要,这就是python 导入的行为。提醒,只有属于模块最高级的代码才是全局变量,全局类,和全局函数声明。
核心笔记:当模块导入后,就执行所有的模块
当导入python模块后,就执行所有的模块!当导入python 模块后,会执行该模块!当你导入foo 模块时候,它运行
所有最高级别的(即没有缩进的)python 代码,比如,'main()’。如果foo 含有bar 函数的声明,那么便执行def foo(...)。如何处理那些不想每次导入都执行的代码呢?缩进它,并放入if __name__ == '__main__' 的内部。导入模块的副作用是导致最高级代码运行。
在某些情况下,可能需要用不同全局和局部的名字空间集合,而不是默认的集合来执行模块。execfile() 函数的语法非常类似于eval()函数的。
execfile(filename, globals=globals(), locals=locals())
类似eval(),globals 和locals 都是可选的,如果不提供参数值的话,默认为执行环境的名字空间。如果只给定globals,那么locals 默认和globals 相同。如果提供locals 值的话,它可以是任何映射对象[一个定义/覆盖了__getitem__()的对象]。
注意:(在修改的时候)小心局部名字空间。比较安全的做法是传入一个虚假的"locals"字典并检查是否有副作用。execfile()不保证不会修改局部名字空间。
python2.4 里加入了一个新的命令行选项(或开关),允许从shell 或DOS 提示符,直接把模块作为脚本来执行。当以脚本的方式来书写你的模块的时候,执行它们是很容易的。你可以使用行从你的工作目录调用你的脚本。——$ myScript.py # or $ python myScript.py
5. 执行其他(非Python)程序
执行外部程序os模块函数 | |
(u 只对unix 有效, w 只对windows 有效) | |
os 模块函数 | 描述 |
system(cmd) | 执行程序cmd(字符串),等待程序结束,返回退出代码(windows 下,始终为0) |
fork() | 创建一个和父进程并行的子进程[通常来说和exec*()一起使用];返回两次....一次给父进程一次给子进程 |
execl(file, arg0,arg1,...) | 用参数列表arg0, arg1 等等执行文件 |
execv(file, arglist) | 除了使用参数向量列表,其他的和execl()相同 |
execle(file, arg0,arg1,... env) | 和execl 相同,但提供了环境变量字典env |
execve(file,arglist, env) | 除了带有参数向量列表,其他的和execle()相同 |
execlp(cmd, arg0,arg1,...) | 于execl()相同,但是在用户的搜索路径下搜索完全的文件路径名 |
execvp(cmd, arglist) | 除了带有参数向量列表,与execlp()相同 |
execlpe(cmd, arg0, arg1,... env) | 和execlp 相同,但提供了环境变量字典env |
execvpe(cmd,arglist, env) | 和execvp 相同,但提供了环境变量字典env |
spawn*(mode, file, args[, env]) | spawn*()家族在一个新的进程中执行路径,args 作为参数,也许还有环境变量的字典env;模式(mode)是个显示不同操作模式的魔术。 |
wait() | 等待子进程完成[通常和fock 和exec*()一起使用] |
waitpid(pid,options) | 等待指定的子进程完成[通常和fock 和exec*()一起使用] |
popen(cmd, mode='r',buffering=-1) | 执行字符串cmd,返回一个类文件对象作为运行程序通信句柄,默认为读取模式和默认系统缓冲 |
startfile(path) | 用关联的应用程序执行路径 W |
system()函数,接收字符串形式的系统命令并执行它。当执行命令的时候,python 的运行是挂起的。当我们的执行完成之后,将会以system()的返回值形式给出退出状态,python 的执行也会继续。system()保留了现有的标准文件,包括标准的输出,意味着执行任何的命令和程序显示输出都会传到标准输出上。system()通常和不会产生输出的命令一起使用,其中的一些命令包括了压缩或转换文件的程序,挂载磁盘到系统的程序,或其他执行特定任务的命令---通过退出状态显示成功或失败而不是通过输入和/或输出通信。通常的约定是利用退出状态,0 表示成功,非零表示其他类型的错误。
popen()函数是文件对象和system()函数的结合。它工作方式和system()相同,但它可以建立一个指向那个程序的单向连接,然后如访问文件一样访问这个程序。如果程序要求输入,那么你要用'w'模式写入那个命令来调用popen()。你发送给程序的数据会通过标准输入接收到。同样地,'r'模式允许spawn 命令,那么当它写入标准输出的时候,你就可以通过类文件句柄使用熟悉的file 对象的read*()方法来读取输入。就像对于文件,当使用完毕以后,你应当close()连接。
popen()返回一个类文件对象;注意readline(),往往,保留输入文本行尾的newline字符。
fork()采用称为进程的单一执行流程控制,:用户系统同时接管了两个fork——也就是说让用户拥有了两个连续且并行的程序。调用fork()的原始进程称为父进程,而作为该调用结果新创建的进程则称为子进程。当子进程返回的时候,其返回值永远是0;当父进程返回时,其返回值永远是子进程的进程标识符(又称进程ID,或PID)(这样父进程就可以监控所有的子进程了)PID 也是唯一可以区分他们的方式!创建另外一个进程的主要目的是为了运行其他程序,所以我们必须在父进程和子进程返回时采取分流措施。正如上面我们所说,它们的PID 是不同的,而这正是我们区分它们的方法。
所有的exec*()函数装载文件或者命令,并用参数列表(分别给出或作为参数列表的一部分)来执行它。如果适用的话,也可以给命令提供环境变量字典。所有版本的exec*()都会用给定文件作为现在要执行的程序取代当前(子)进程的Python 解释器。
函数spawn*()家族和fork,exec*()相似,因为它们在新进程中执行命令;然而,你不需要分别调用两个函数来创建进程,并让这个进程执行命令。你只需调用一次spawn*()家族。由于其简单性,你放弃了“跟踪”父进程和子进程执行的能力;该模型类似于在线程中启动函数。还有点不同的是你必须知道传入spawn*()的魔法模式参数。
各种文件执行函数 | |
文件对象 属性 | 描述 |
os/popen2.popen2() | 执行文件,打开文件,从新创建的运行程序读取(stdout),或者向该程序写(stdin) |
os/popen2.popen3() | 执行文件,打开文件,从新创建的运行程序读取(stdout 和stder) ,或者向该程序写(stdin) |
os/popen2.popen4() | 执行文件,打开文件,从新创建的运行程序读取(结合stdout,stdout),或者向该程序写(stdin) |
commands.getoutput() | 在子进程中执行文件,以字符串返回所有的输出 |
subprocess.call() | 创建subprocess 的便捷函数。 Popen 等待命令完成,然后返回状态代码;与os.system()类似,但是是较灵活的替代方案 |
6. 结束执行
在python 中,有各种应对错误的方法。其中之一便是通过异常和异常处理。另外一个方法便是建造一个“清扫器”方法,这样便可以把代码的主要部分放在if 语句里,在没有错误的情况下执行,因而可以让错误的情况“正常地“终结。然而,有时也需要在退出调用程序的时候,返回错误代码以表明发生何种事件。
立即退出程序并返回调用程序的主要方式是sys 模块中的exit()函数。sys.exit()的语法为:
sys.exit(status=0)
当调用sys.exit()时,就会引发systemExit()异常。除非对异常进行监控(在一个try 语句和合适的except 子句中),异常通常是不会被捕捉到或处理的,解释器会用给定的状态参数退出,如果没有给出的话,该参数默认为0。System Exit 是唯一不看作错误的异常。它仅仅表示要退出python的愿望。sys.exit()经常用在命令调用的中途发现错误之后,比如,如果参数不正确,无效,或者参数数目不正确。
调用sys.exit()使python 解释器退出。exit()的任何整数参数都会以退出状态返回给调用者,该值默认为0。
sys.exitfunc()默认是不可用的,但你可以改写它以提供额外的功能。当调用了sys.exit()并
sys.exitfunc()默认是不可用的,但你可以改写它以提供额外的功能。当调用了sys.exit()并在解释器退出之前,就会用到这个函数了。这个函数不带任何参数的,所以你创建的函数也应该是无参的。如果sys.exitfunc 已经被先前定义的exit 函数覆盖了,最好的方法是把这段代码作为你exit()函数的一部分来执行。一般说来,exit 函数用于执行某些类型的关闭活动,比如关闭文件和网络连接,最好用于完成维护任务,比如释放先前保留的系统资源。
os 模块的_exit()函数不应该在一般应用中使用。其语法为:
os._exit(status)
这个函数提供的功能与sys.exit()和sys.exitfunc()相反,根本不执行任何清理便立即退出python。与sys.exit()不同,状态参数是必需的。通过sys.exit()退出是退出解释器的首选方法。
os 模块的kill()函数模拟传统的unix 函数来发送信号给进程。kill()参数是进程标识数(PID)和你想要发送到进程的信号。发送的典型信号为SIGINT, SIGQUIT,或更彻底地,SIGKILL,来使进程终结。
7. 各种操作系统接口
各种os模块属性 | |
os 模块属性 | 描述 |
uname() | 获得系统信息(主机名,操作系统版本,补丁级别, 系统构架等等) |
getuid()/setuid(uid) | 获取/设置现在进程的真正的用户ID |
getpid()/getppid() | 获取真正的现在/父进程ID(PID) |
getgid()/setgid(gid) | 获取/设置现在进程的群组ID |
getsid()/setsid() | 获取会话ID(SID)或创建和返回新的SID。 |
umask(mask) | 设置现在的数字unmask,同时返回先前的那个(mask 用于文件许可) |
getenv(ev)/putenv(ev, value),environ | 获取和设置 环境变量ev 的值;os.envion 属性是描述当前所有环境变量的字典 |
geteuid()/setegid() | 获取/设置当前进程的有效用户ID(GID) |
getegid()/setegid() | 获取/设置当前进程的有效组ID(GID) |
getpgid(pid)/setpgid(pid, pgrp) | 获取和设置进程GID 进程PID;对于get,如果pid 为0,便返回现在进程的进程GID |
getlogin() | 返回运行现在进程的用户登录 |
times() | 返回各种进程时期的元组 |
strerror(code) | 返回和错误代码对应的错误信息 |
getloadavg() | 返回代表在过去1,5,15 分钟内的系统平均负载值的元组。 |
执行环境相关模块 | |
模块 | 描述 |
atexit | 注册当python 解释器退出时候的执行句柄 |
popen2 | 提供额外的在os.popen 之上的功能:(提供通过标准文件和其他的进程交互的能力;对于python2.4 和更新的版本,使用subpross) |
commands | 提供额外的在os.system 之上的功能:把所有的程序输出保存在返回的字符串中(与输出到屏幕的相反);对于python2.4 和更新的版本,使用subpross |
getopt | 在这样的应用程序中的处理选项和命令行参数 |
site | 处理site-specific 模块或包 |
platform | 底层平台和架构的属性 |
subprocess | 管理(计划替代旧的函数和模块,比如os.system(), os.spawn*(),os.popen*(), popen2.*, command.*) |