2015/10/9 Python基础(21):可调用和可执行对象
在Python中有多种运行外部程序的方法,比如,运行操作系统命令或另外的Python脚本,或执行一个磁盘上的文件,或通过网络来运行文件。这完全取决于想要干什么。特定的环境包括:
在当前脚本继续运行
创建和管理子进程
执行外部命令或程序
执行需要输入的命令
通过网络来调用命令
执行命令来创建需要处理的输出
执行其他的Python脚本
执行一系列动态生成的Python语句
导入Python模块
Python中,内建和外部模块都可以提供上述各种功能。程序员得根据实现的需要,从这些模块中选择合适的处理方法。
可调用对象
许多Python对象都是我们所说的可调用的,即是任何通过函数操作符()来调用的对象。Python有4中可调用对象:函数,方法,类,以及一些类的实例。
1.函数
Python有3中不同类型的函数对象,第一种是内建函数。
内建函数(BIFs)
BIF是用C/CPP写的,编译过后放入Python解释器,然后把它们作为第一(内建)名字空间的一部分加载进系统。这些函数在_builtin_模块里,并作为__builtins__模块导入到解释器中。
可以用dir()列出函数的所有属性:
>>> dir(type)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
从内部机制来看,因为BIFs和内建方法(BIMs)属于相同类型,所以对BIF或者BIM调用type()的结果是:
>>> type(dir)
<type 'builtin_function_or_method'>
用户定义的函数(UDF)
UDF通常是用Python写的,定义在模块的最高级,因此会作为全局名字空间的一部分装置到系统中。函数也可以在其他函数体内定义,我们可以对多重嵌套作用域中的属性进行访问。
从内部机制来看,用户自定义的函数是“函数”类型的:
>>> def foo():pass >>> type(foo)
<type 'function'>
lambda表达式
lambda表达式和用户自定义函数相比,略有不同。虽然它们也是返回一个函数对象,但是不是用def语句创建的,而是用lambda关键字:
因为lambda表达式没有给命名绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,然后就可以直接调用或者再通过函数来调用。变量仅是个别名,并不是函数对象的名字。
通过lambda来创建函数的对象除了没有命名之外,和UDF有相同的属性;__name__或者func_name属性给定位字符串"<lambda>"
>>> lambdaFunc = lambda x: x * 2
>>> lambdaFunc(12)
24
>>> type(lambdaFunc)
<type 'function'>
>>> lambdaFunc.__name__
'<lambda>'
如果是UDF的名字,则是这样:
>>> def foo():pass >>> foo.__name__
'foo'
以上是三种函数对象
2.方法
用户自定义方法是被定义为类的一部分的函数。许多Python的数据类型,比如列表和字典,也有方法,被称为内建方法。为了说明所有权的类型,方法通过对象的名字和句点属性标识符命名。
内建方法(BIMs)
刚刚我们说了BIF和BIM的类似之处。只有内建类型有内建方法。对于内建方法,type()工厂函数给出了和BIF一样的输出。
>>> type([].append)
<type 'builtin_function_or_method'>
此外BIM和BIF两者有相同属性。不同之处在于BIM的__self__属性指向一个Python对象,BIF指向None。
用户定义的方法(UDM)
UDM包含在类定义之中,只是拥有标准函数的包装,仅有定义他们的类可以使用。如果没有在子类定义中被覆盖,也可以通过子类实例来调用它们。
3.类
调用类的结果就是创建了实例,也就是实例化。
4.类的实例
Python给类提供了名为__call__的特别方法,该方法允许程序员创建可调用的对象(实例)。默认情况下,__call__()方法是没有实现的,这意味着大多数情况下实例是不可调用的。然而,如果在类中覆盖了这个方法,那么这个类的实例就成为可调用的了。调用这样的实例对象等同于调用__call__()方法。如:foo()和foo.__call__(foo)的效果相同,这里的foo也作为参数出现,因为是对自己的引用,实例将自动成为每次方法调用的第一个参数,如果__call__()有参数,那么foo(arg)就和foo.__call__(foo, arg)一样。
代码对象
可调用对象是Python执行环境里最重要的部分,然而这并不是全部。Python语句,赋值,表达式,甚至还有模块构成了更宏大的场面。这些可执行对象无法像可调用物那样被调用。这些代码块被称为代码对象。
每个可调用物的核心都是代码对象,由语句,赋值,表达式,以及其他可调用物组成。查看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码分成语句,赋值,表达式,以及可调用物。可调用物又可以递归分解到下一层,那里有它自己的代码对象。
一般来说,代码对象可以作为函数或者方法调用的一部分来执行,也可用exec语句或内建函数eval()来执行。从整体上看,一个Python模块的代码对象是构成该模块的全部代码。
如果要执行Python代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,它们不包含任何关于它们执行环境的信息,这便是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息。
UDF有 udf.func_code 属性就是代码对象。UDM的udm.im_func也是一个函数对象,他同样有它自己的udm.im_func.func_code代码对象。这样的话,你会发现,函数对象仅是代码对象的包装,方法则是给函数对象的包装。当研究到最底层,便是一个代码对象。
可执行的对象声明和内建函数
Python提供了大量的BIF来支持可调用/可执行对象。
1.callable()
callable()是一个布尔函数,确定一个对象是否可以用函数操作符()来调用。如果可调用便返回True,否则便是False。
2.compile()
compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用exec语句或者内建函数eval()来执行这些对象或者他们进行求值。
compile的三个参数都是必需的,第一参数代表了要编译的Python代码。第二个参数是字符串,虽然是必需的,但通常被置为空串,该参数代表了存放代码对象的文件的名字(字符串类型)。compile的通常用法是动态生成字符串形式的Python代码,然后生成一个代码对象——代码显然没有存放在任何文件。最后的参数是个字符串,用来表明代码的类型。有三个可能值:
'eval' 可求值的表达式[和eval()一起使用]
'single' 单一可执行语句[和exec一起使用]
'exec' 可执行与剧组[和exec一起使用]
可求值表达式
>>> eval_code = compile('10-2','','eval')
>>> eval(eval_code)
8
单一可执行语句
>>> single_code = compile('print "Hello world"','','single')
>>> single_code
<code object <module> at 024DC698, file "", line 1>
>>> exec single_code
Hello world
可执行语句组
>>> exec_code = compile("""
req = input('Count how many numbers?')
for eachNum in range(req):
print eachNum
""",'','exec')
>>> exec exec_code
Count how many numbers?6
0
1
2
3
4
5
3.eval()
eval()对表达式求值,表达式可以为字符串或内建函数compile()创建的预编译代码对象。这个对象是第一个也是最重要的参数。第二个和第三个参数是可选的,分别代表了全局和局部名称空间中的对象。如果给出了这两个参数,全局必须是个字典,局部可以是任意的映射对象。如果没有给出这两个参数,分别默认为globals()和locals()返回的对象。如果只传入了一个全局字典,那么该字典也作为局部参数传入。
这是eval()的一个例子。
>>> eval('')
123
>>> int('')
123
>>> eval('123+234')
357
>>> int('123+234') Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
int('123+234')
ValueError: invalid literal for int() with base 10: '123+234'
开始,我们传入'123'给eval()和int()的时候,返回了相同的结果,但是方式是不尽相同的,eval()接受引号内的字符串把它作为Python表达式求值,int()接受代表整数的字符串并把它转换为整数。而当我们输入'123+234'时,情况就不一样了。int()调用就失败了。可以认为eval()函数对表达式两端的引号视而不见,将它执行在解释器上,返回结果。
4.exec
和eval()相似,exec语句执行代码对象或字符串形式的Python代码。类似地,用compile()预编译重复代码有助于改善性能,因为在调用时不必经过字节编译处理。exec只接受一个参数,语法是:
exec obj
obj可以是原始的字符串,比如单一语句或语句组,也可以预编译层一个代码对象。
>>> exec """
x = 0
print 'x is currently:',x
while x < 5:
x += 1
print 'incrementing x to:',x
"""
x is currently: 0
incrementing x to: 1
incrementing x to: 2
incrementing x to: 3
incrementing x to: 4
incrementing x to: 5
exec还可以接受有效的Python文件对象。如果我们用上面的多行代码创建一个xcount.py的文件,那么也可以这样执行相同代码:
>>> f = open('xcount.py') # open the file
>>> exec f # execute the file
x is currently: 0
incrementing x to: 1
incrementing x to: 2
incrementing x to: 3
incrementing x to: 4
incrementing x to: 5
上面我们调用了文件f,如果在完成后继续调用它
>>> exec f
>>>
调用会失败。并不是真正的失败,只是不再做任何事。事实上,exec已从文件中读取了全部数据且停留在文件末尾(EOF)。当用相同的文件对象对exec进行调用的时候,没有可执行的代码了,所以exec什么都不做。
我们可以用tell()方法来告诉我们处于文件的何处,然后用os.path.getsize()来告诉我们脚本由多大。然后就会发现,这两个数字完全一样:
>>> f.tell()
116
>>> f.close()
>>> from os.path import getsize
>>> getsize('xcount.py')
116
如果想在不关闭和重新打开文件的情况下再次运行它,可以用seek()到文件最开头并再次调用exec。假定我们还没有调用f.close(),那么:
>>> f.seek(0)
>>> exec f
x is currently: 0
incrementing x to: 1
incrementing x to: 2
incrementing x to: 3
incrementing x to: 4
incrementing x to: 5
>>> f.close()
5.input()
之前用到的内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input()),input()和raw_input()一样有一个可选的参数给用户字符串提示。
input不同于raw_input(),input()返回的数据是对输入表达式求值的结果,是一个Python对象。
6.使用Python在运行时生成和执行Python代码
书上提供了两个例子,这两个例子在运行时吧Python代码作为字符串并执行。
第一个例子是loopmake.py脚本。一个简单迅速和执行循环的计算机辅助软件工程。提示用户给出各种参数,生成代码字符串,并执行它。
dashes = '\n' + '-' * 50
exec_dict = {
'f':''' #for loop
for %s in %s:
print %s
''', 's':''' # sequence while loop
%s = 0
%s = %s
while %s < len(%s):
print %s[%s]
%s = %s + 1
''', 'n':''' # counting while loop
%s = %d
while %s < %d:
print %s
%s = %s + %d
'''
} def main(): ltype = raw_input('Loop type? (For/While)')
dtype = raw_input('Data type? (Number/Sequence)') if dtype == 'n':
start = input('Starting value? ')
stop = input('Ending value (non-inclusive)? ')
step = input('Stepping value? ')
seq = str(range(start, stop, step)) else:
seq = raw_input('Enter sequence:') var = raw_input('Iterative variable name?') if ltype == 'f':
exec_str = exec_dict['f'] % (var, seq, var) elif ltype == 'w':
if dtype == 's':
svar = raw_input('Enter sequence name? ')
exec_str = exec_dict['s'] % \
(var, svar, seq, var, svar, svar, var, var, var)
elif dtype == 'n':
exec_str = exec_dict['n'] % \
(var, start, var, stop, var, var, var, step) print dashes
print 'The custom-generated code for you is:' + dashes
print exec_str + dashes
print 'The execution of the code:' + dashes
exec exec_str
print dashes if __name__ == '__main__':
main()
有兴趣的人可以执行一下这段代码,十分有趣,可以帮助你生成代码并执行。反正我写这段代码的时候感觉到了exec和input的强大。
第二个例子是有条件地执行代码
这是代码:
def foo():
return True def bar():
'bar() does not do much'
return True foo.__doc__ = 'foo() does not do much'
foo.tester = '''
if foo():
print 'PASSED'
else:
print 'FAILED'
''' for eachAttr in dir():
obj = eval(eachAttr)
if isinstance(obj, type(foo)):
if hasattr(obj, '__doc__'):
print '\nFunction "%s" has a doc string:\n\t%s'\
% (eachAttr, obj.__doc__)
if hasattr(obj, 'tester'):
print 'Function "%s" has a tester... executing'\
% eachAttr
exec obj.tester
else:
print 'Function "%s" has no tester... skipping'\
% eachAttr
else:
print '"%s" is not a function' % eachAttr
下面是执行后的结果:
>>>
"__builtins__" is not a function
"__doc__" is not a function
"__file__" is not a function
"__name__" is not a function
"__package__" is not a function Function "bar" has a doc string:
bar() does not do much
Function "bar" has no tester... skipping Function "foo" has a doc string:
foo() does not do much
Function "foo" has a tester... executing
PASSED
代码并不难理解,但其所做的事的确很有趣不是么?
2015/10/9 Python基础(21):可调用和可执行对象的更多相关文章
- 二十一. Python基础(21)--Python基础(21)
二十一. Python基础(21)--Python基础(21) 1 ● 类的命名空间 #对于类的静态属性: #类.属性: 调用的就是类中的属性 #对象.属性: 先从自己的内存空间里找名 ...
- 2015/9/9 Python基础(10):文件和输入输出
文件对象文件对象不仅可以用来访问普通的磁盘文件,而且也可以访问其它任何类型抽象层面上的“文件”.一旦设置了合适的“钩子”,你就可以访问文件类型接口的其它对象,就好像访问的是普通文件一样.文件对象的处理 ...
- 2015/10/9 Python核编初级部分学习总结
终于在十一长假之后的两天看完了<Python核心编程>的初级部分.虽然到后来两章,类和环境看得越来越慢,越来越难以理解.很多东西只能靠强记,也没办法真正掌握了,我想了想,还是不强迫自己去背 ...
- 10个Python基础练习项目,你可能不会想到练手教程还这么有趣
美国20世纪最重要的实用主义哲学家约翰·杜威提出一个学习方法,叫做:Learning By Doing,在实践中精进.胡适.陶行知.张伯苓.蒋梦麟等都曾是他的学生,杜威的哲学也影响了蔡元培.晏阳初等人 ...
- python基础--------字符串的调用详解(2)
Python 字符串的的调用方法~~~@@@ 17. strip : 去除字符串左右两边指定的字符 18. rstrip : 去除字符串右边指定的字符 19 . lstrip : 去除 ...
- python 基础 ------字符串的调用详解(1)
Python 字符串的的调用方法~~~ 废话不多说直接奔主题 >>>>>>>>>>>>>>>>> ...
- 2015/9/29 Python基础(20):类的授权
类的授权 1.包装包装在Python编程世界中时经常会被提到的一个术语.它是一个通用的名字,意思是对一个已存在的对象进行包装,不管它是数据类型,还是一段代码,可以是对一个已存在的对象,增加新的,删除不 ...
- 2015/9/20 Python基础(16):类和实例
面向对象编程编程的发展已经从简单控制流中按步的指令序列进入到更有组织的方式中,依靠代码块可以形成命名子程序和完成既定的功能.结构化的或过程性编程可以让我们把程序组织成逻辑快,以便重复或重用.创造程序的 ...
- python基础-9.1 面向对象进阶 super 类对象成员 类属性 私有属性 查找源码类对象步骤 类特殊成员 isinstance issubclass 异常处理
上一篇文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象 ...
随机推荐
- Alpha冲刺——第五天
Alpha第五天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...
- hdu1242 Rescue DFS(路径探索题)
这里我定义的路径探索题指 找某路能够到达目的地,每次走都有方向,由于是探索性的走 之后要后退 那些走过的状态都还原掉 地址:http://acm.hdu.edu.cn/showproblem.php? ...
- Spring的初始化:org.springframework.web.context.ContextLoaderListener
在web.xml中配置 <listener> <listener-class>org.springframework.web.context.ContextLoaderL ...
- Communications link failure--分析之(JDBC的多种超时情况)
本文是针对特定的情景下的特定错误,不是所有Communications link failure错误都是这个引起的,重要的区分特点是:程序是不是在卡主后两个小时(服务器的设置)后程序才感知到,才抛出了 ...
- 【php】session读写锁
事件:a文件中操作$_SESSION['start'] = 'yes'; sleep(100); 休眠100s 在这休眠的时间段中,b文件操作$_SESSION['start'] = 'no'; 结 ...
- PHP执行原理
简单解释:PHP执行原理 客户端向服务器发送一个请求,如果请求的是一个HTML页面,服务器直接将HTML页面发送到客户端给浏览器解析,如果请求的是PHP页面,则服务器会运行PHP页面然后生成标准的HT ...
- 框架整合小小总结【SSH】注解式
Spring 注解式注册 bean: 大致分为以下几步: 开启 context 空间支持 开启自动扫描功能,指定扫描包路径 使用注解配置 bean (使用@Component 注解) 给 bean 注 ...
- Java多线程 -yield用法
前几天复习了一下多线程,发现有许多网上讲的都很抽象,所以,自己把网上的一些案例总结了一下! 一. Thread.yield( )方法: 使当前线程从执行状态(运行状态)变为可执行态(就绪状态).cpu ...
- EM算法【转】
混合高斯模型和EM算法 这篇讨论使用期望最大化算法(Expectation-Maximization)来进行密度估计(density estimation). 与K-means一样,给定的训练样本是, ...
- [AT2557] [arc073_c] Ball Coloring
题目链接 AtCoder:https://arc073.contest.atcoder.jp/tasks/arc073_c 洛谷:https://www.luogu.org/problemnew/sh ...