作为《Python核心编程》核心部分的最后一章,这篇的内容也相当重要。对于高级部分的整理,将采用《Python核心编程》第三版,但是,方式会以之前的完全不一样了。
1.可调用对象
可调用对象即可通过函数操作符“()”来调用的对象,也可以通过函数式编程接口来进行调用,如apply()、filter()、map()和reduce()。Python有4种可调用对象:函数、方法、类和一些类的实例。
(1)函数
Python中有三种不同类型的函数:内建函数(BIF)、用户定义的函数(UDF)和lambda表达式。
-
内建函数(BIF)
用C/C++写的,编译过后放入Python解释器中,然后把它们作为第一(内建)名称空间的一部分加载进系统,这些函数在__builtin__模块里,并作为__builtins__模块导入到解释器中。
可以使用dir()来列出函数的所有属性,另外从内部机制来看,BIF和内建方法属于相同的类型,所以全长type()调用的结果是:
1
2
3
4
5
|
>>>
type
(
dir
)
<
type
'builtin_function_or_method'
>
>>>
>>>
type
(
'xpleaf'
.upper)
<
type
'builtin_function_or_method'
>
|
但是应用于工厂函数,得到的结果是不一样的,虽然这些工厂函数本身是内建函数:
1
2
|
>>>
type
(
int
)
<
type
'type'
>
|
那是因为,本质上这些工厂函数是类。
-
用户定义的函数(UDF)
用户定义的函数通常是用Python写的,调用type()的结果如下:
1
2
3
4
|
>>>
def
foo():
pass
...
>>>
type
(foo)
<
type
'function'
>
|
UDF本身也有很多属性:
1
2
|
>>>
dir
(foo)
[
'__call__'
,
'__class__'
,
'__closure__'
,
'__code__'
,
'__defaults__'
,
'__delattr__'
,
'__dict__'
,
'__doc__'
,
'__format__'
,
'__get__'
,
'__getattribute__'
,
'__globals__'
,
'__hash__'
,
'__init__'
,
'__module__'
,
'__name__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
,
'func_closure'
,
'func_code'
,
'func_defaults'
,
'func_dict'
,
'func_doc'
,
'func_globals'
,
'func_name'
]
|
-
lambda表达式(名为“<lambdda>”的函数)
lambda返回一个函数对象,只是lambda表达式没有给命令绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,通过该变量来进行调用,只是需要注意的是,这个变量仅仅是个别名,并不是函数对象的名字。
通过lambda来创建的函数对象除了没有命名外,和用户定义的函数是有相同的属性的,当然,对于__name__属性,值为"<lambda>":
1
2
3
4
5
6
7
8
9
10
11
|
>>> lambdaFunc
=
lambda
x: x
*
2
>>> lambdaFunc(
100
)
200
>>>
type
(lambdaFunc)
<
type
'function'
>
>>>
>>> lambdaFunc.__name__
'<lambda>'
>>>
>>> foo.__name__
'foo'
|
(2)方法
用户自定义方法是被定义为类的一部分函数,而许多Python数据类型本身也有方法,这些方法被称为内建方法。
-
内建方法(BIM)
只有内建类型有内建方法,如下:
1
2
|
>>>
type
([].append)
<
type
'builtin_function_or_method'
>
|
BIM和BIF两者享有相同的属性,只是不同的是,BIM的__self__属性指向一个Python对象,而BIF指向None:
1
2
3
4
5
6
7
8
9
|
>>>
dir
([].append)
[
'__call__'
,
'__class__'
,
'__cmp__'
,
'__delattr__'
,
'__doc__'
,
'__eq__'
,
'__format__'
,
'__ge__'
,
'__getattribute__'
,
'__gt__'
,
'__hash__'
,
'__init__'
,
'__le__'
,
'__lt__'
,
'__module__'
,
'__name__'
,
'__ne__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__self__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
]
>>> [].append.__self__
[]
>>>
dir
.__self__
>>>
print
[].append.__self__
[]
>>>
print
dir
.__self__
None
|
-
用户定义的方法(UDM)
所有的UDM都是相同的类型,即“实例方法”,无论该方法是否有绑定的实例:
1
2
3
4
5
6
7
8
9
10
|
>>>
class
C(
object
):
...
def
foo(
self
):
...
pass
...
>>> c
=
C()
>>>
>>>
type
(C.foo)
<
type
'instancemethod'
>
>>>
type
(c.foo)
<
type
'instancemethod'
>
|
只是访问对象本身会揭示该方法是否绑定:
1
2
3
4
|
>>> c.foo
<bound method C.foo of <__main__.C
object
at
0x7f37a3c45810
>>
>>> C.foo
<unbound method C.foo>
|
(3)类
调用类,其实就是创建一个实例对象。类有默认构造器,但是这个函数什么都不做,可以通过实现__init__()方法来自定义实例化过程。
(4)类的实例
Python给类提供了名为__call__的特别方法,该方法允许程序创建可调用的对象(实例)。默认情况下没有实现该方法,因此实例是不可调用的。
在定义类时覆盖__call__方法即可达到实例可调用的效果,这样,调用实例对象就等同于调用__call__()方法,参数的设置跟类的构造器原理是一样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
>>>
class
C(
object
):
...
def
__call__(
self
,
*
args):
...
print
"I'm callable! Called with args:\n"
, args
...
>>> c
=
C()
>>> c
<__main__.C
object
at
0x7f37a3c2d090
>
>>>
callable
(c)
# 实例是可调用的
True
>>> c()
# 调用实例
I'm
callable
! Called with args:
()
>>> c(
3
)
# 传入参数
I'm
callable
! Called with args:
(
3
,)
>>> c(
3
,
'no more , no less'
)
I'm
callable
! Called with args:
(
3
,
'no more , no less'
)
>>>
>>> c.__call__(
3
)
# 与c(3)的调用原理是一样的
I'm
callable
! Called with args:
(
3
,)
>>> c.__call__(
3
,
'no more, no less'
)
I'm
callable
! Called with args:
(
3
,
'no more, no less'
)
|
2.代码对象
每个可调用物的核心都是代码对象,由语句、赋值、表达式和其他可调用物组成。查看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码可以分成语句、赋值、表达式,以及可调用物。可调用物又可以递归分解到下一层,那儿有自己的代码对象。
一般来说,代码对象可以作为函数或者方法调用的一部分来执行(比如语句或赋值等),也可用exec语句或内建函数eval()来执行。从整体上看,一个Python模块的代码对象是构成该模块的全部代码。
如果要执行Python代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,它们不包含任何关于它们执行环境的信息,这便是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息(如属性等相关信息)。
函数对象仅是代码对象的包装,方法则是给函数对象的包装,只是不同于单纯的代码对象,它们还提供了一些额外的信息。当研究到最底层,便会发现这是一个代码对象。
在函数对象中,有一个func_code属性,其实就是指代码对象:
1
2
3
4
|
>>>
def
foo():
pass
...
>>> foo.func_code
<code
object
foo at
0x7f37a5daceb0
,
file
"<stdin>"
, line
1
>
|
3.可执行的对象声明和内建函数
常见的相关函数如下:
可执行的对象声明和内建函数 |
|
内建函数和语句 | 描述 |
callable(obj) | 如果obj可调用,返回True,否则返回False |
compile(string, file, type) | 从type类型中创建代码对象;file是代码存放的地方(通常设为"") |
eval(obj, globals=globals(), locals=locals()) | 对obj进行求值,obj是已编译为代码对象的表达式,或是一个字符串表达式;可以给出全局或者/和局部的名称空间 |
exec obj | 执行obj、单一的Python语句或者语句的集合,也就是说格式是代码对象或者字符串;obj也可以是一个文件对象(已经打开的有效的Python脚本中) |
input(prompt='') | 等同于eval(raw_input(prompt='')) |
(1)callable()
callable()是一个布尔函数,确定一个对象是否可以通过函数操作符(())来调用:
1
2
3
4
5
6
7
8
|
>>>
callable
(
dir
)
True
>>>
callable
(
1
)
False
>>>
def
foo():
pass
...
>>>
callable
(foo)
True
|
(2)compile()
compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用exec语句或者内建函数eval()来执行这些代码对象或者对它们进行求值。一个很重要的观点是:exec和eval()都可以执行字符串格式的Python代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile()函数提供了一次性字节代码预编译,以后每次调用的时候,就不用编译了。
compile的三个参数都是必需的,第一参数借助了要编译的Python代码。第二个字符串,虽然是必需的,但通常被设置为空串。该参数代表了存放代码对象的文件的名字(字符串类型)。compile的通常用法是动态生成字符串形式的Python代码,然后生成一个代码对象——代码显然没有存放在任何文件,所以文件名就通常设置为空了。
最后的参数是个字符串,它用来表明代码对象的类型,有三个可能值:
'eval' | 可求值的表达式,和eval()一起使用 |
'single' | 单一可执行语句,和exec一起使用 |
'exec' | 可执行语句组,和exec一起使用 |
-
可求值表达式
1
2
3
4
5
|
>>> eval_code
=
compile
(
'100+200'
, '
', '
eval
')
>>> eval_code
<code
object
<module> at
0x7f37a5db3cb0
,
file
"", line
1
>
>>>
eval
(eval_code)
300
|
-
单一可执行语句
1
2
3
4
5
|
>>> single_code
=
compile
(
'print "Hello world!"'
, '
', '
single')
>>> single_code
<code
object
<module> at
0x7f37a3c2a0b0
,
file
"", line
1
>
>>>
exec
single_code
Hello world!
|
-
可执行语句组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> 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()返回的对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>> a
=
3
>>>
eval
(
'a'
)
3
>>>
def
test_eval():
... a
=
6
...
print
eval
(
'a'
)
...
>>> test_eval()
6
>>
>>> scope
=
{}
>>> scope[
'a'
]
=
3
>>> scope[
'b'
]
=
4
>>> result
=
eval
(
'a+b'
,scope)
>>> result
7
|
(4)exec
exec语句只接受一个参数,即exec obj,obj除了可以是原始的字符串(单一语句或语句组)或是compile()编译的代码对象外,也可以是文件对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
>>> f
=
open
(
'xcount.py'
)
>>>
for
eachLine
in
f:
...
print
eachLine,
...
x
=
0
print
'x is currently:'
, x
while
x <
5
:
x
+
=
1
print
'incrementing x to:'
, x
>>>
>>> f.tell()
86
>>> 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.tell()
# 执行完之后,就指针就停留在文件末尾(end-of-line,EOF)
86
|
(5)input()
内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input()),这说明input()和raw_input()本身还是有区别的。
从功能上看,input不同于raw_input(),因为raw_input()总是以字符串的形式返回用户的输入;input()履行相同的任务,而且它还把输入作为Python表达式进行求值。这意味着input()返回的数据是对输入表达式求值的结果:一个Python对象。
举例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
>>> aString
=
raw_input
(
'Enter a list: '
)
Enter a
list
: [
'xpleaf'
,
'clyyh'
]
>>> aString
"['xpleaf', 'clyyh']"
>>>
type
(aString)
<
type
'str'
>
>>>
>>> aList
=
input
(
'Enter a list: '
)
Enter a
list
: [
'xpleaf'
,
'clyyh'
]
>>> aList
[
'xpleaf'
,
'clyyh'
]
>>>
type
(aList)
<
type
'list'
>
|
(6)使用Python在运行时生成和执行Python代码
有需要时可以参考书本上的例子。
本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1795129,如需转载请自行联系原作者