Python 核心编程[第十四章]
0x00可调用对象
许多 Python 对象都是可调用的,任何能通过函数操作符 “()” 来调用的对象。要调用可调用对象,函数操作符得紧跟在可调用对象之后。 Python 中有四种可调用对象:函数、方法、类、以及一些类的实例。这些对象的任何引用或者别名都是可调用的。
一、函数
Python 有三种不同类型函数对象。内建函数,用户定义函数,lambda表达式。
1.内建函数
内建函数是用 C/C++ 写的,编译过后放入 Python 解释器,然后把它们作为第一名称空间的一部分加载进系统。这些函数在 _bulitin_ 模块里,并作为 _builtins_ 模块导入到解释器中。
内建函数属性 | 描述 |
---|---|
bif.__doc__ | 文档字符串 |
bif.__name__ | 字符串类型的文档名字 |
bif.__self__ | 设置为 None (保留给内建方法) |
bif.__module__ | 存放 bif 定义的模块名字 |
可以使用 dir() 列出函数的所有属性,从内部机制来看,并且 BIF 和 BIM(内建方法) 属于相同的类型.
2.用户定义函数
用户定义的函数通常是用 Python 写的,定义在模块的最高级,因此会作为全局名称空间的一部分装载到系统中。
用户自定义函数属性 | 描述 |
---|---|
udf.__doc__ | 文档字符串 |
udf.__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 | 包含了自由变量的引用的单元对象元组 |
二、方法
1.内建方法
内建方法与内建函数类似,只有内建类型有内建方法。内建函数和内建方法也享有相同属性。不同之处在于内建方法的 __self__属性指向一个 Python 对象,而内建方法指向 None。对于类和实例,都能以该对象为参数,通过内建函数 dir() 来获取他们的数据和方法属性,这也可以用在内建方法上。
内建方法的属性 | 描述 |
---|---|
bim.__doc__ | 文档字符串 |
bim.__name__ | 字符串类型的函数名字 |
bim.__self__ | 绑定对象 |
2.用户定义方法
用户定义的方法包含在类定义之中,,只是拥有标准函数的包装,仅有定义它们的类可以使用。在没有被字类定义覆盖掉的情况下,也可以通过字类实例调用它们。用户定义的方法是与类对象相关联的,但是只能通过类的实例来调用。
属性 | 描述 |
---|---|
udm.__doc__ | 文档字符串 |
udm.__name__ | 字符串类型的类的名字方法 |
udm.__module__ | 定义 udm 的模块的名字 |
udm.im_class | 方法相关联的类 |
udm.im_func | 方法的函数对象 |
udm.im_self | 如果绑定的话为相关联的实例,如果非绑定为 None |
三、类
我们可以利用类的可调用性来创建实例。“调用”类的结果便是创建了实例,即实例化。类有默认构造器,该函数什么都不做,基本上只有一个 pass 语句。程序员可以通过实现 __init__方法,来自定义实例化过程。实例化调用的任何参数都会传到构造器里。
四、类的实例
Python 给类提供了名为 __call__ 的特别方法,该方法允许程序员创建可调用的对象。默认情况下 __call__ 方法是没有实现的,这意味着大多数实例都是不可调用的。然而,如果类定义中覆盖了这个方法,那么这个类的实例就成为可调用的了。调用这样的实例对象等同于调用 __call__ 方法。自然地,任何在实例调用中给出的参数都会被传入到 __call__ 中。那么 foo() 就和 foo.__call__(foo) 的效果相同,这里 foo 也作为参数出现,因为是对自己的引用,实例将自动为每次方法调用的第一个参数。如果 __call__() 有参数,比如 (self,arg),那么 foo(arg) 就和调用 foo.__call__(foo,arg)一样。
class C(object):
def __call__(self,*args):
print("Hello",args)
# 实例化
c = C()
# 调用实例
c()
0x01代码对象
Python 语句、赋值、表达式、模块等,这些可执行对象无法像可调用物那样被调用。每个可调用物的核心都是代码对象,由语句、赋值、表达式和其他可调用物组成。可调用物又可以递归分解到下一层,那有自己的代码对象。
代码对象可以作为函数或方法调用的一部分来执行,也可以用 exec 语句或内建函数 eval() 来执行。如果要执行 Pyhton 代码,那么该代码必须要先转换成字节码。这才是真正的代码对象。不过,它们不包含任何关于他们执行环境的信息,这边是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息。
udf.func_code 属性所表示的就是代码对象。因为 udm.im_func 也是一个函数对象,所以他同样有自己的 udm.im_func.func_code 代码对象。所以,函数对象仅是代码对象的包装,方法则是给函数对象的包装。
0x02可执行的对象声明和内建函数
可执行的内建函数和语句 | 描述 |
---|---|
callable(obj) | 如果 obj 可调用,返回 True ,否则返回 False |
compile(string,file,type) | 从 type 类型中 创建代码对象;file 是代码存放的地方 (通常设置为"") |
eval(obj,globals=globals(),locals=locals() | 对 obj 进行求值,obj 是已编译为代码对象的表达式,或是一个字符串表达式;可以给出全局/局部的名称空间 |
exec obj | 执行 obj、单一的 Python 语句或者语句的集合,也就是说格式是代码对象或者字符串;obj 也可以是一个文件对象 |
input(prompt="") | 等同于 eval(raw_input(prompt="")) |
一、callable()
它是一个布尔函数,确定一个对象是否可以通过函数操作符 “()” 来调用。如果可以便返回 True 否则返回 False。
二、compile()
compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用 exec 语句或内建函数 eval() 来执行这些对象或者对他们进行求值。一个很重要的观点是:exec 和 eval() 都可以执行字符串格式的 Python 代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile() 函数提供了一次性字节代码预编译,以后每次调用的时候都不用编译了。
compile 的三个参数都是必须的,第一个参数代表了要编译的 Python 代码。第二个字符串代表了存放代码对象的文件的名字。最后的参数是一个字符串,它用来表明代码对象的类型。有三个可能值:‘eval’ 可求值的表达式[和 eval() 一起使用] ‘single’ 单一可执行语句[和 exec 一起使用] ‘exec’ 可执行语句组 [和 exec 一起使用]
1.可求值表达式
code = compile('100 + 200'.'','eval')
eval(code)
2.单一可执行语句
code = compile('print('Hello'),'','single'')
exec code
3可执行语句组
code = compile("""a = 'Hello
print(a)
'""",'',''exec)
exec code
三、eval()
对表达式求值,第一个参数可以是字符串或者 complie() 创建的预编译代码对象。第二个和第三个参数都是可选,分别代表了全局和局部名称空间中的对象。globals 必须是一个字典,local 可以是任意的映射对象。
eval('520 + 1')
四、exec
和 eval() 相似,exec 语句执行代码对象或字符串形式的 Python 代码。
# exec 还可以接收有效的 Python 文件对象。
# xxx.py
print('Hello')
# a.py
f = open('xxx.py','r')
exec f
# 注意:一旦调用过一次之后,继续调用将会失效。
# 即,不再运行 xxx.py 中的代码,因为这个时候 exec 已从文件中读取了全部的数据且停留在文件的末尾。
0x03执行
一、执行其他 Python 程序
1.导入
第一次导入模块会执行模块最高级的代码(没有缩进的代码),只有属于模块最高级的代码才是全局变量,全局类和全局函数的声明。不过可以把每次导入不过并不想执行的代码放进 if __name__ == '__main__'的内部。
2.execfile()
执行一个文件中的代码
#语法
# execfile(filename,globals = globals(), locals = locals())
# 其中的用法与 eval 类似
f = open('xxx.py','r')
exec f
f.close()
<=>
execfile(filename)
3.将模块作为脚本执行
在命令行中执行,python -m 选项 和 -c 选项。当用 -c 导入某个模块时,该模块下 if __name__ == ‘__main__’ 中的代码不会被自动执行(此时导入的模块只是当作一个模块被执行)。所以可以用 -m 选项把某个模块作为一个脚本文件执行(此时该模块被当作一个脚本执行,相对 ‘__main__’ 便可以自己执行)。
二、执行其他非 Python 程序
其他程序包括二进制可执行文件或其他脚本语言的源代码。要有可执行环境:允许文件访问和执行,脚本文件必须能访问它们的解释器(perl\bash),二进制文件必须可访问。
1. os.system()
接收字符串形式的系统命令,并执行它。当执行命令的时候,Python 的运行是挂起的。当我们的执行完成之后,会以 system() 的返回值形式给出退出状态, Python 的执行也会继续。
system() 保留了现有的便准文件,包括标准的输出,意味着执行任何命令和程序显示输出都会传达到标准输出上。
system() 通常和不会产生输出的命令一起使用,其中的一些命令包括了压缩和转化文件的程序,挂载磁盘到系统的程序,或其他执行特定任务的命令-----通过退出状态显示成功或失败而不是通过输入/输出通信。通常的约定是利用退出状态,0 表示成功,非 0 表示其他类型的错误。
2. os.popen
popen() 函数是文件对象和 system() 函数的结合。他工作方式和 system() 相同,但它可以建立一个指向那个个程序的单项连接,然后像访问文件一样访问这个程序。如果程序要求输入,那么你要用 ‘w’ 模式写入那个命令来调用 popen()。‘r’ 模式允许 spawn 命令,那么当它写入标准输出的时候,你就可以通过类文件句柄使用熟悉的 file 对象的 read() 方法来读取输入。就像对于文件,当你使用完毕,你应该 close() 连接。
3. os.fork()、os.exec()、os.wait()
4. os.spawn*()
三、结束执行
1. sys.exit() and SystemExit
立刻退出程序并返回调用程序。当调用 sys.exit() 时,就会引发 systemExit() 异常。该异常通常是不会被捕捉到或处理的,解释器会用给定的状态参数退出,如果没有给出的话,该参数默认为 0 .System Exit 是唯一不看做错误的异常。通过 sys.exit() 退出是退出解释器的首选方法。
2.sysexitfunc()
该函数默认是不可用的,但是你可以改写它以提供额外的功能。当调用了 sys.exit() 并在解释器退出之前,就会用到这个函数了。这个函数不带任何参数的,所以你创建的函数也应该是无参的。
如果 sys.exitfunc 已经被先前定义的 exit 函数覆盖了,最好的方法是把这段代码作为 exit() 函数的一部分来执行。一般来说,exit 函数用于执行某些类型的关闭活动,比如关闭文件和网络连接,最好用于完成维护任务,比如解释先前保留的系统资源。
3. os._exit() 函数
os 模块的 _exit() 函数不应该在一般应用中使用(只适用于特定平台 Unix Win32 等)。这个函数提供的功能与 sys.exit() 和 sys.exitfunc() 相反,根本不执行任何清理便立刻退出 Python 程序。与 sys.exit() 不同,状态参数是必须的。
os._exit(status)
4. os.kill() Function
os 模块的 kill() 函数模拟传统的 unix 函数来发送信号给进程。kill() 参数是进程表示数(PID)和你想要发送进程的信号。发送的典型信号为 SIGINT、SIGQUIT 或 SIGKILL,来使进程终结。