第四章 自省的威力
本章论述了
下面是一个完整可运行的
例 4.1. apihelper.py
如果您还没有下载本书附带的样例程序, 可以
def info(object, spacing=10, collapse=1):![]()
![]()
"""Print methods and doc strings. Takes module, class, list, dictionary, or string.""" methodList = [method for method in dir(object) if callable(getattr(object, method))] 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 info.__doc__
![]() | 该模块有一个声明为 |
![]() | info |
![]() | 函数内的代码是缩进形式的。 |
![]() | if |
![]() | if |
info
例 4.2. apihelper.py 的用法示例
>>> from apihelper import info >>> li = [] >>> info(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 value 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
缺省地,程序输出进行了格式化处理,以使其易于阅读。多行
例 4.3. apihelper.py 的高级用法
>>> import odbchelper >>> info(odbchelper) buildConnectionString Build a connection string from a dictionary Returns string. >>> info(odbchelper, 30) buildConnectionString Build a connection string from a dictionary Returns string. >>> info(odbchelper, 30, 0) buildConnectionString Build a connection string from a dictionary Returns string.
4.2. 使用可选参数和命名参数
Python
info
def info(object, spacing=10, collapse=1):
spacing
假如你要指定
例 4.4. info 的有效调用
info(odbchelper)info(odbchelper, 12)
info(odbchelper, collapse=0)
info(spacing=15, object=odbchelper)
![]()
这些看上去非常累,除非你意识到参数不过是一个字典。“通常” 不使用参数名称的函数调用只是一个简写的形式,Python
![]() | |
调用函数时唯一必须做的事情就是为每一个必备参数指定值 (以某种方式);以何种具体的方式和顺序都取决于你。 |
4.3. 使用 type、str、dir 和其它内置函数
Python
type
str
例 4.6. str 介绍
>>> str(1)'1' >>> horsemen = ['war', 'pestilence', 'famine'] >>> 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'
info
例 4.7. 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 |
![]() | d |
![]() | 这里就是真正变得有趣的地方。odbchelper |
最后是
例 4.8. callable 介绍
>>> import string >>> string.punctuation'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' >>> string.join
<function join at 00C55A7C> >>> callable(string.punctuation)
False >>> callable(string.join)
True >>> print string.join.__doc__
join(list [,sep]) -> string Return a string composed of the words in list, with intervening occurrences of sep. The default separator is a single space. (joinfields and join are synonymous)
![]() | string |
![]() | string.join |
![]() | string.punctuation |
![]() | string.join |
![]() | 任何可调用的对象都有 |
type、str、dir
像这样考虑的好处是,你是可以获取
例 4.9. 内置属性和内置函数
>>> from apihelper import info >>> import __builtin__ >>> info(__builtin__, 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 |
4.4. 通过 getattr 获取对象引用
你已经知道
例 4.10. 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'
![]() | 该语句获取列表的 |
![]() | 该语句也是返回 |
![]() | 如果不确信它是多么的有用,试试这个:getattr |
![]() | getattr |
![]() | 理论上,getattr |
getattr
例 4.11. apihelper.py 中的 getattr 函数
>>> 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 True >>> callable(getattr(object, method))
True
![]() | 该语句返回 |
![]() | 使用 |
![]() | 接下来的是你真正用在 |
![]() | 在这个例子中,method |
![]() | 由于 |
getattr
例如,让我们假设有一个以
例 4.12. 使用getattr 创建分发者
import statsout def output(data, format="text"):output_function = getattr(statsout, "output_%s" % format)
return output_function(data)
![]()
你是否发现前面示例的一个 Bug?即字符串和函数之间的松耦合,而且没有错误检查。如果用户传入一个格式参数,但是在
值得庆幸的是,getattr
例 4.13. getattr 缺省值
import statsout def output(data, format="text"): output_function = getattr(statsout, "output_%s" % format, statsout.output_text) return output_function(data)![]()
正如你所看到,getattr
4.5. 过滤列表
如你所知,Python
过滤列表语法:
[mapping-expression for element in source-list if filter-expression]
这是你所知所爱的列表解析的扩展。前三部分都是相同的;最后一部分,以
例 4.14. 列表过滤介绍
>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"] >>> [elem for elem in li if len(elem) > 1]['mpilgrim', 'foo'] >>> [elem for elem in li if elem != "b"]
['a', 'mpilgrim', 'foo', 'c', 'd', 'd'] >>> [elem for elem in li if li.count(elem) == 1]
['a', 'mpilgrim', 'foo', 'c']
回到
methodList = [method for method in dir(object) if callable(getattr(object, method))]
这行看上去挺复杂――确实也很复杂――但是基本结构都还是一样的。整个过滤表达式返回一个列表,并赋值给
过滤表达式看上去很恐怖,其实不是。你已经知道了
所以这个表达式接收一个名为
4.6. and 和 or 的特殊性质
在Python
例 4.15. and 介绍
>>> 'a' and 'b''b' >>> '' and 'b'
'' >>> 'a' and 'b' and 'c'
'c'
![]() | 使用 |
![]() | 如果布尔环境中的某个值为假,则 |
![]() | 所有值都为真,所以 |
例 4.16. or 介绍
>>> 'a' or 'b''a' >>> '' or 'b'
'b' >>> '' or [] or {}
{} >>> def sidefx(): ... print "in sidefx()" ... return 1 >>> 'a' or sidefx()
'a'
如果你是一名
例 4.17. and-or 技巧介绍
>>> a = "first" >>> b = "second" >>> 1 and a or b'first' >>> 0 and a or b
'second'
然而,由于这种
and-or
在
到现在为止,这个技巧可能看上去问题超过了它的价值。毕竟,使用
4.7. 使用 lambda 函数
Python
例 4.20. lambda 函数介绍
>>> def f(x): ... return x*2 ... >>> f(3) 6 >>> g = lambda x: x*2>>> g(3) 6 >>> (lambda x: x*2)(3)
6
总的来说,lambda
![]() | |
lambda |
apihelper.py
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
注意这里使用了
还要注意的是使用了没有参数的
例 4.21. split 不带参数
>>> s = "this is\na\ttest">>> print s this is a test >>> print s.split()
['this', 'is', 'a', 'test'] >>> print " ".join(s.split())
'this is a test'
![]() | 这是一个多行字符串,通过使用转义字符的定义代替了三重引号。\n |
![]() | 不带参数的 |
![]() | 通过 |
那么
processFunc
在一个不很健壮的语言中实现它,像
4.8. 全部放在一起
最后一行代码是唯一还没有解释过的,它完成全部的工作。但是现在工作已经简单了,因为所需要的每件事都已经按照需求建立好了。所有的多米诺骨牌已经就位,到了将它们推倒的时候了。
下面是
print "\n".join(["%s %s" % (method.ljust(spacing), processFunc(str(getattr(object, method).__doc__))) for method in methodList])
注意这是一条命令,被分隔成了多行,但是并没有使用续行符 (\)。还记得我说过一些表达式可以分割成多行而不需要使用反斜线吗?列表解析就是这些表达式之一,因为整个表达式包括在方括号里。
现在,让我们从后向前分析。
for method in methodList
告诉我们这是一个列表解析。如你所知
例 4.22. 动态得到 doc string
>>> import odbchelper >>> object = odbchelper>>> method = 'buildConnectionString'
>>> getattr(object, method)
<function buildConnectionString at 010D6D74> >>> print getattr(object, method).__doc__
Build a connection string from a dictionary of parameters. Returns string.
![]() | 在 |
![]() | 在你遍历 |
![]() | 通过 |
![]() | 现在,很容易就可以打印出方法的 |
接下来令人困惑的是
例 4.23. 为什么对一个 doc string 使用 str ?
>>> >>> def foo(): print 2 >>> >>> foo() 2 >>> >>> foo.__doc__>>> foo.__doc__ == None
True >>> str(foo.__doc__)
'None'
![]() | |
在 |
现在你确保有了一个字符串,可以把这个字符串传给
再往回走一步,你再一次使用了字符串格式化来连接
例 4.24. ljust 方法介绍
>>> s = 'buildConnectionString' >>> s.ljust(30)'buildConnectionString ' >>> s.ljust(20)
'buildConnectionString'
几乎已经完成了。有了
上述就是最后一个令人困惑的地方了。但是现在你应该已经理解这段代码了。
4.9. 小结
apihelper.py
def info(object, spacing=10, collapse=1): """Print methods and doc strings. Takes module, class, list, dictionary, or string.""" methodList = [method for method in dir(object) if callable(getattr(object, method))] 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 info.__doc__
apihelper.py
>>> from apihelper import info >>> li = [] >>> info(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 value 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