Linux 版 (精华区)
发信人: emacs (被淹死的鱼), 信区: Linux
标 题: 第二章 自省的威力 -- 1
发信站: 哈工大紫丁香 (Sat Jun 15 09:49:00 2002) , 转信
2.1. 接触
本章涉及了Python的强项之一:自省。正如你已经知道了,在Python中的每件事物都是对
象,自省就是指代码可以在内存中象处理对象一样查找其它的模块和函数,得到它们的信
息,并且对它们进行操作。用这种方法,可以定义无名的函数,不按参数的顺序调用函数
,并且引用甚至事先并不知道名字的函数。
下面是一个完整的,可运行的Python程序。看到它,你应该对其理解很多了。用数字标出
的行阐述了在开始深入Python所涉及的一些概念。如果剩下的代码看上去有点奇怪不用担
心,当读完本章之后你就会都学会了。
例 2.1. apihelper.py
from types import BuiltinFunctionType, BuiltinMethodType, \
FunctionType, MethodType, ClassType
def help(object, spacing=10, collapse=1):
"""Print methods and doc strings.
Takes module, class, list, dictionary, or string."""
typeList = (BuiltinFunctionType, BuiltinMethodType, FunctionType, MethodTy
pe, ClassType)
methodList = [method for method in dir(object) if type(getattr(object, met
hod)) in typeList]
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s
)
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__))
for method in methodList])
if __name__ == "__main__":
print help.__doc__ 这个模块有一个函数, help。根据它的函数声明,它接收三个
参数: object,spacing,和 collapse。最后两个实际上是可选参数,很快就会看到。
help 函数有一个多行文档字符串,它描述了函数的作用。注意没有提到返回值,这个函
数将只使用它的效果而不是它的值。
函数内的代码是缩排的。
if __name__ 技巧允许这个程序在独立运行的时候做些有用的事情,不会在别的程序将
它做为模块使用而引起冲突。在这个例子中,程序简单地打印出 help 函数的文档字符串
。
if 语句使用 == 进行比较,并且不需要小括号。
help 函数是为象你这样的程序员,当工作在Python IDE环境下设计来使用的。它接收任何
拥有函数或方法的对象(象模块,它拥有函数,或列表,它拥有方法),接着打印出对象所
有的函数和文档字符串。
例 2.2. apihelper.py 的用法举例
>>> from apihelper import help
>>> li = []
>>> help(li)
append L.append(object) -- append object to end
count L.count(value) -> integer -- return number of occurrences of value
extend L.extend(list) -- extend list by appending list elements
index L.index(value) -> integer -- return index of first occurrence of va
lue
insert L.insert(index, object) -- insert object before index
pop L.pop([index]) -> item -- remove and return item at index (default
last)
remove L.remove(value) -- remove first occurrence of value
reverse L.reverse() -- reverse *IN PLACE*
sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1
, 0, 1缺省地,输出进行了格式化处理以便容易阅读。多行文档字符串被合并成单行,但
是这个选项可以被改变,通过将 collapse 参数设为0。如果函数的名字超过10个字符,可
以将 spacing 参数设为一个更长的值,来使输出更容易阅读。
例 2.3. apihelper.py 的高级用法
>>> import odbchelper
>>> help(odbchelper)
buildConnectionString Build a connection string from a dictionary Returns stri
ng.
>>> help(odbchelper, 30)
buildConnectionString Build a connection string from a dictionary Ret
urns string.
>>> help(odbchelper, 30, 0)
buildConnectionString Build a connection string from a dictionary
Returns string.
2.2. 使用 from module import 导入模块
Python有两种导入模块的方法。两种都有用,你应该知道什么时候使用哪一种方法。一种
方法, import module,你已经在第一章看过了。另一种方法完成同样的事情,但是它以
细微但重要的不同方式工作。
例 2.4. 基本的 from module import 语法
from types import BuiltinFunctionType, BuiltinMethodType, \
FunctionType, MethodType, ClassType
这种语法与你所知的 import module 语法类似,但是有一个重要的区别:导入模块 type
s 的属性和方法被直接导入到局部名字空间中,所以这些属性和方法是直接有效的,不需
要通过模块名来限定。
例 2.5. import module 对比 from module import
>>> import types
>>> types.FunctionType
<type 'function'>
>>> FunctionType
Traceback (innermost last):
File "<interactive input>", line 1, in ?
NameError: There is no variable named 'FunctionType'
>>> from types import FunctionType
>>> FunctionType
<type 'function'> types 模块不包含方法,只有每个Python对象类型的属性。注意,
FunctionType 属性必需用 types 模块名进行限定。
FunctionType 本身没有被定义在当前名字空间中;它只存在于 types 的上下文环境中
。
这个语法直接从 types 模块中导入 FunctionType 属性到局部名字空间。
现在 FunctionType 可以直接存取,不需要引用 types。
什么时候你应该使用 from module import?
如果你要经常存取模块的属性和方法,且不想一遍遍地敲入模块名,使用 from module i
mport。
如果你想要有选择地导入某些属性和方法,而不想要其它的,使用 from module import。
如果模块包含的属性和方法与你的某个模块同名,你必须使用 import module 来避免名字
冲突。
另外,它只是风格问题,你会看到用两种方式编写的Python代码。
2.3. 可选和定名参数
Python允许函数参数拥有缺省值;如果函数不带参数调用,参数就得到它的缺省值。而且
,参数可以通过使用指定参数名(译注:此后译作定名参数)以任何次序使用。在SQL Serve
r Transact/SQL中的存储过程可以做到这点,如果你是一个SQL Server脚本的头头,你可
以跳过这部分。
例 2.6. help, 一个有两个可选参数的函数
def help(object, spacing=10, collapse=1):spacing 和 collapse 是可选的,因为它们
拥有定义了的缺省值。object 是必须的,因为它没有缺省值。如果只使用一个参数来调用
help,spacing 缺省为 10 且 collapse 缺省为 1。如果用两个参数调用 help,collap
se 仍然缺省为 1。
假定你想要给 collapse 指定一个值,但是想接受 spacing 的缺省值。在大多数语言中,
可能很不幸,因为你将不得不使用三个参数来调用函数。但在Python中,参数可以通过名
字以任意顺序被指定。
例 2.7. help 的合法调用
help(odbchelper)
help(odbchelper, 12)
help(odbchelper, collapse=0)
help(spacing=15, object=odbchelper) 只用一个参数,spacing 得到它的缺省值 10
且 collapse 得到它的缺省值 1。
使用两个参数,collapse 得到它的缺省值 1。
这里你明确指明了 collapse 参数,且给定了它的值。spacing 仍然得到它的缺省值 1
0。
甚至必须的参数(象 object,它没有缺省值)可以被定名,并且定名参数可以以任意顺序
显示。
这些看上去非常累,除非你意识到参数不过是一个字典。“通常”的不使用参数名字的函
数调用方式只是一个简写,Python按照函数声明中所指定的顺序来将参数值与参数名匹配
起来。并且大多数情况,你会按“通常”的方式来调用函数,但是如果你需要总是可以有
额外的灵活性。
要调用一个函数唯一要做的就是必须为每个必须的参数指定一个值(多多少少);以何种方
式和顺序来指定就是你的事了。
2.4. type,str,dir,和其它内置函数
Python有小部分相当有用的内置函数。其它的所有函数都划分到不同的模块中去了。这一
点实际上是有意识的设计决定,是为了保证核心语言避免象其它脚本语言一样变得臃肿(咳
,咳,VB)。
type 函数返回任意对象的数据类型。可能的类型列在了 types 模块内。这些类型对帮助
者(helper)函数有用,可以处理几种数据类型。
例 2.8. type 介绍
>>> type(1)
<type 'int'>
>>> li = []
>>> type(li)
<type 'list'>
>>> import odbchelper
>>> type(odbchelper)
<type 'module'>
>>> import types
>>> type(odbchelper) == types.ModuleType
1 type 接收任何事物,并且返回它的数据类型。我的意思是任何东西。整数,字符串,
列表,字典,序列,函数,类,模块,甚至类型。
type 可以接收变量并返回它的数据类型。
type 也可以用于模块。
你可以使用 types 模块中的常量用来比较对象的类型。这就是 help 函数所做的,这一
点很快会看到。
str 强制将数据转化为字符串。每一种数据类型都可以被转化为字符串。
例 2.9. str 介绍
>>> str(1)
'1'
>>> horsemen = ['war', 'pestilence', 'famine']
>>> horsemen.append('Powerbuilder')
>>> str(horsemen)
"['war', 'pestilence', 'famine', 'Powerbuilder']"
>>> str(odbchelper)
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>"
>>> str(None)
'None' 对于象整数这样的简单类型,你会认为 str 可以工作,因为几乎每一种语言都有
一个用来将整数据转化为字符串的函数。
然而 str 可工作于任何类型的任何对象。这里它应用于一个我们零碎构造的列表。
str 也可用于模块。注意,模块的字符串表示包含了模块在磁盘上的路径,所以你的结
果看上去可能不一样。
str 的一个细小但很重要的行为是它可以用于 None,Python空值。它返回字符串 'Non
e'。我们将应用这一点在 help 函数中来满足我们的需要,这一点我们很快会看到。
help 函数的核心是强大的 dir 函数。dir 返回任意一个对象的属性和方法的列表:模块
,函数,字符串,列表,字典...相当多的东西。
例 2.10. dir 介绍
>>> li = []
>>> dir(li)
['append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', '
sort']
>>> d = {}
>>> dir(d)
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', '
values']
>>> import odbchelper
>>> dir(odbchelper)
['__builtins__', '__doc__', '__file__', '__name__', 'buildConnectionString']
li 是一个列表,所以 dir(li) 返回列表所有方法的一个列表。注意返回的列表包含着字
符串表示的方法的名字,而不是方法自身。
d 是一个字典,所以 dir(d) 返回字典方法的名字的列表。其中至少有一个方法,keys
,应该看上去熟悉。
这里就是真正变得有趣的地方。odbchelper 是一个模块,所以 dir(odbchelper) 返回
在模块中定义的所有东西的列表,包括内置属性,象 __name__ 和 __doc__,和你定义的
属性和方法其它什么的。在这个例子中,odbchelper 仅有一个用户自定义方法,buildCo
nnectionString 函数,我们在开始了解Python中学过的。
type,str,dir,和所有其它的Python内置函数被组合进一个名叫 __builtins__ 的特别
模块中。(它在前后有两个下划线。)如果有帮助的话,你可以认为Python在启动时自动地
执行 from __builtins__ import * ,这样就导入了所有内置的函数到名字空间中,所以
你可以直接使用它们。
象这样考虑的好处是,你可以通过得到 __builtins__ 的信息,作为一个组,存取所有内
置的函数和属性。猜到什么了吗?我们有一个实现那个功能的函数,它叫做 help。自已试
一下然后现在完全忽略列表;在后面我们将深入到一些更重要的功能。(一些内置错误类,
象 AttributeError,应该看上去已经熟悉了。)
例 2.11. 内置属性和函数
>>> from apihelper import help
>>> help(__builtins__, 20)
ArithmeticError Base class for arithmetic errors.
AssertionError Assertion failed.
AttributeError Attribute not found.
EOFError Read beyond end of file.
EnvironmentError Base class for I/O related errors.
Exception Common base class for all exceptions.
FloatingPointError Floating point operation failed.
IOError I/O operation failed.
[...snip...]
Python附带了出色的参考手册,你应该十分仔细地阅读它们,学习Python所提供的所有的
模块。对于大多数语言,你会发现自已要经常回头参考手册(或man文档,或上帝来帮助你
,MSDN)来提醒自已如何使用这些模块,而Python大部分是自我文档化的。
2.5. 使用 getattr 得到对象的各种引用
你已经知道Python函数是对象。你不知道的是,使用 getattr 函数,你可以得到一个直到
运行时才知道的函数的引用。
例 2.12. getattr 介绍
>>> li = ["Larry", "Curly"]
>>> li.pop
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop")
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe")
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear")
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop")
Traceback (innermost last):
File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop' 这样得到列表的 pop 方法
的一个引用。注意这样不是调用 pop 方法,调用应是 li.pop()。它是方法本身。
这样也返回 pop 方法的引用,但这次,方法的名字被指定为 getattr 函数的一个字符
串参数。 getattr 是一个相当有用的内置函数,它可以返回任意对象的任意属性。在本例
中,对象是列表,属性是 pop 方法。
万一不是深信它是多么的有用,试试这个:getattr 的返回值是方法,接着你可以调用
它就好象直接调用 li.append("Moe")。但是你没有直接调用函数,而是把函数名当成字符
串来指定。
getattr 也可以用于字典。
在理论上,getattr 应可以用于序列,除了序列没有方法,所以不管给出什么属性名,
getattr 都将引发一个异常。
getattr 不只适用于内置数据类型。它也可用于模块。
例 2.13. getattr 用于 apihelper.py
>>> import odbchelper
>>> odbchelper.buildConnectionString
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString")
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method)
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method))
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
1
它返回在 odbchelper 模块中的 buildConnectionString 函数的引用, odbchelper 模
块我们在开始了解Python中学过了。(你看到的16进制地址是专对我的机器的;你的输出将
有所不同。)
使用 getattr,我们可以得到同一函数的同一引用。通常, getattr(object, "attrib
ute") 相当于 object.attribute。如果 object 是一个模块,那么 attribute 可以是定
义在模块中的任何东西:一个函数,类,或全局变量。
并且这个就是我们真正用在 help 函数中的东西。object 作为一个参数被传到函数中;
method 是一个字符串,这个字符串是方法或函数的名字。
在本例中,method 是函数的名字,这一点我们可以通过得到它的类型来证实。
--
※ 来源:.哈工大紫丁香 http://bbs.hit.edu.cn [FROM: 211.93.34.115]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:201.327毫秒