python 学习笔记四

自省

使用  type、st r、dir  和其它内置函数

type 函数返回任意对象的数据类型。在 types 模块中列出了可能的数据类型。这对于处理多种数据类型的帮助者函数非常有用。

>>> type(1)    
<type 'int'>
>>> li = []
>>> type(li)    
<type 'list'>

 

str 将数据强制转换为字符串。每种数据类型都可以强制转换为字符串。

>>> str(1)          (1)
'1'
>>> horsemen = ['war', 'pestilence', 'famine']
>>> horsemen
['war', 'pestilence', 'famine']
>>> horsemen.append('Powerbuilder')
>>> str(horsemen)   (2)
"['war', 'pestilence', 'famine', 'Powerbuilder']"
>>> str(odbchelper) (3)
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>
>>> str(None)       (4)
'None'

 

 

Example 4.7.  dir  介绍
>>> li = []
>>> dir(li)           (1)
['append', 'count', 'extend', 'index', 'insert',
'pop', 'remove', 'reverse', 'sort']
>>> d = {}
>>> dir(d)            (2)
['clear', 'copy', 'get', 'has_key', 'i tems', 'keys', 'setdefault', 'update', 'values']
>>> import odbchelper
>>> dir(odbchelper)   (3)
['__builtins__', '__doc__', '__file__', '__name__', 'bui ldConnectionString']

 

最后是 callable 函数,它接收任何对象作为参数,如果参数对象是可调用的,
返回 True;否则返回 False。可调用对象包括函数、类方法,甚至类自身 (下一
章将更多的关注类)。
Example 4.8.  call abl e 介绍
>>> import string
>>> string.punctuation           (1)
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
>>> string.join                  (2)
<function join at 00C55A7C>
>>> callable(string.punctuation) (3)
False
>>> callable(string.join)        (4)
True
>>> print string.join.__doc__    (5)
- 54 -Dive Into Python http://diveintopython.org/
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)

 

 

4.3.3. 内置函数
type、str、dir 和其它的 Python 内置函数都归组到了 __builtin__ (前后分别是双下划线) 这个特殊的模块中。如果有帮助的话,你可以认为 Python 在启动时自动执行了 from __builtin__ import *,此语句将所有的 “内置” 函数导入该命名空间,所以在这个命名空间中可以直接使用这些内置函数。
像这样考虑的好处是,你是可以获取 __builtin__ 模块信息的,并以组的形式访问所有的内置函数和属性。猜到什么了吗,现在我们的 Python 有一个称为info 的函数。自己尝试一下,略看一下结果列表。后面我们将深入到一些更重要的函数。(一些内置的错误类,比如 AttributeError,应该看上去已经很熟悉了。)

 

 

Example 4.10. getattr 介绍
>>> li = ["Larry", "Curly"]
>>> li.pop                       (1)
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop")           (2)
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe") (3)
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear")         (4)
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop")           (5)
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop'

4.4.1.  用于模块的  getattr
getattr 不仅仅适用于内置数据类型,也可作用于模块。

Example 4.11. apihelp er.py  中的  getatt r 函数
>>> import odbchelper
>>> odbchelper.buildConnectionString             (1)
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString") (2)
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method)                      (3)
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method))                (4)
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
True
>>> callable(getattr(object, method))            (5)
True
(1)  该语句返回 odbchelper 模块中 buildConnectionString 函数的引用,Chapter         2,    
第一个     Python     程序     你已经研习过这个方法了。(你看到的这个十六进制地
址是我机器上的;你的输出结果会有所不同。)
(2)  使用 getattr,你能够获得同一函数的同一引用。通常,getattr(object,
"attribute") 等价于 object.attribute。如果 object 是一个模块的话,那么 attribute 可能是定义在模块中的任何东西:函数、类或者全局变量。
(3)  接下来的是你真正用在 info 函数中的东西。object 作为一个参数传递给函数; method 是方法或者函数的名称字符串。
(4)  在这个例子中,method 是函数的名称,通过获取 type 可以进行验证。
(5)  由于 method 是一个函数,所以它是可调用的。4.4.1.  用于模块的  getattr getattr 不仅仅适用于内置数据类型,也可作用于模块。

 

Example 4.12. 使用 getatt r 创建分发者
import statsout
def output(data, format="text"):                              (1)
    output_function = getattr(statsout, "output_%s" % format) (2)
    return output_function(data)                              (3)
(1)  output 函数接收一个必备参数 data,和一个可选参数 format。如果没有指定
format 参数,其缺省值是 text 并完成普通文本输出函数的调用。
(2)  你可以连接 format 参数值和 "output_" 来创建一个函数名称作为参数值,
然后从 statsout 模块中取得该函数。这种方式允许今后很容易地扩展程序
以支持其它的输出格式,而且无需修改分发函数。所要做的仅仅是向
statsout 中添加一个函数,比如 output_pdf,之后只要将 “pdf” 作为 format 的
参数值传递给 output 函数即可。
(3)  现在你可以简单地调用输出函数,就像调用其它函数一样。output_function
变量是指向 statsout 模块中相应函数的引用。

 

Example 4.13. getatt r 缺省值
import statsout
def output(data, format="text"):
    output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
    return output_function(data) (1)
(1)  这个函数调用一定可以工作,因为你在调用 getattr 时添加了第三个参数。
第三个参数是一个缺省返回值,如果第二个参数指定的属性或者方法没能
找到,则将返回这个缺省返回值。正如你所看到,getattr 是相当强大的。它是自省的核心,在后面的章节中你将看到它更强大的示例

 

4.5.  过滤列表
如你所知,Python 具有通过列表解析 (Section         3.6, “    映射     list”    ) 将列表映射到
其它列表的强大能力。这种能力同过滤机制结合使用,使列表中的有些元素
被映射的同时跳过另外一些元素。
过滤列表语法:
[mapping-expression for element in source-list if filter-expression]
这是你所知所爱的列表解析的扩展。前三部分都是相同的;最后一部分,以 if
开头的是过滤器表达式。过滤器表达式可以是返回值为真或者假的任何表达
式 (在 Python 中是几乎任何东西)。任何经过滤器表达式演算值为真的元素都
可以包含在映射中。其它的元素都将忽略,它们不会进入映射表达式,更不
会包含在输出列表中。
Example 4.14. 列表过滤介绍
>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d ", "d"]
>>> [elem for elem in li if len(elem) > 1]       (1)

['mpilgrim', 'foo']
>>> [elem for elem in li if elem != "b"]         (2)
['a', 'mpilgrim', 'foo', 'c', 'd', 'd']
>>> [elem for elem in li if li.count(elem) == 1] (3)
['a', 'mpilgrim', 'foo', 'c']
(1)  这里的映射表达式很简单 (只是返回每个元素的值),所以请把注意力集中到过滤器表达式上。由于 Python 会遍历整个列表,它将对每个元素执行
过滤器表达式。如果过滤器表达式演算值为真,该元素就会被映射,同时
映射表达式的结果将包含在返回的列表中。这里,你过滤掉了所有单字符
的字符串,留下了一个由长字符串构成的列表。
(2)  这里你过滤掉了一个特定值 b。注意这个过滤器会过滤掉所有的 b,因为每次取出 b,过滤表达式都将为假。
(3)  count 是一个列表方法,返回某个值在列表中出现的次数。你可以认为这个过滤器将从列表中剔除重复元素,返回一个只包含了在原始列表中有着
唯一值拷贝的列表。但并非如此,因为在原始列表中出现两次的值 (在本
例中,b 和 d) 被完全剔除了。从一个列表中排除重复值有多种方法,但过
滤并不是其中的一种。

 

回到 apihelper.py 中的这一行:
    methodList = [method for method in dir(object) if callable(getattr(object, method))]
这行看上去挺复杂――确实也很复杂――但是基本结构都还是一样的。整个
过滤表达式返回一个列表,并赋值给 methodList 变量。表达式的前半部分是列
表映射部分。映射表达式是一个和遍历元素相同的表达式,因此它返回每个
元素的值。dir(object) 返回 object 对象的属性和方法列表――你正在映射的列表。
所以唯一新出现的部分就是在 if 后面的过滤表达式。
过滤表达式看上去很恐怖,其实不是。你已经知道了 callable、getattr 和 in。正
如你在前面的部分中看到的,如果 object 是一个模块,并且 method 是上述模
块中某个函数的名称,那么表达式 getattr(object, method) 将返回一个函数对象。
所以这个表达式接收一个名为 object 的对象,然后得到它的属性、方法、函数
和其他成员的名称列表,接着过滤掉我们不关心的成员。执行过滤行为是通
过对每个属性/方法/函数的名称调用 getattr 函数取得实际成员的引用,然后
检查这些成员对象是否是可调用的,当然这些可调用的成员对象可能是方法
或者函数,同时也可能是内置的 (比如列表的 pop 方法) 或者用户自定义的 (比
如 odbchelper 模块的 buildConnectionString 函数)。这里你不用关心其它的属性。

 

Example 4.17. and-or  技巧介绍
>>> a = "first"
>>> b = "second"
>>> 1 and a or b (1)
'first'
>>> 0 and a or b (2)
'second'
(1)  这个语法看起来类似于 C 语言中的 bool ? a : b 表达式。整个表达式从左到
右进行演算,所以先进行 and 表达式的演算。1 and 'first' 演算值为 'first',
然后 'first' or 'second' 的演算值为 'first'。
(2)  0 and 'first' 演算值为 False,然后 0 or 'second' 演算值为 'second'。

 

Example 4.19. 安全使用  and-or  技巧
>>> a = ""
>>> b = "second"
>>> (1 and [a] or [b] )[0] (1)
''
(1)  由于 [a] 是一个非空列表,所以它决不会为假。即使 a 是 0 或者 '' 或者其它假值,列表 [a] 也为真,因为它有一个元素。
到现在为止,这个技巧可能看上去问题超过了它的价值。毕竟,使用 if 语句
可以完成相同的事情,那为什么要经历这些麻烦事呢?哦,在很多情况下,
你要在两个常量值中进行选择,由于你知道 a 的值总是为真,所以你可以使
用这种较为简单的语法而且不用担心。对于使用更为复杂的安全形式,依然
有很好的理由要求这样做。例如,在 Python 语言的某些情况下 if 语句是不允许使用的,比如在 lambda 函数中。

 

 

Example 4.20.  lam bda 函数介绍
>>> def f(x):
...     return x*2
...    
>>> f(3)
6
>>> g = lambda x: x*2  (1)
>>> g(3)
6
>>> (lambda x: x*2)(3) (2)
6
(1)  这是一个 lambda 函数,完成同上面普通函数相同的事情。注意这里的简短的语法:在参数列表周围没有括号,而且忽略了 return 关键字 (隐含存在,因为整个函数只有一行)。而且,该函数没有函数名称,但是可以将它赋值给一个变量进行调用。
(2)  使用 lambda 函数时甚至不需要将它赋值给一个变量。这可能不是世上最有用的东西,它只是展示了 lambda 函数只是一个内联函数。总的来说,lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个达式的值。lambda 函数不能包含命令,包含的表达式不能超过一个。不要试图向 lambda 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多长。

 

4.7.1. 真实世界中的  lambda  函数
apihelper.py 中的 lambda 函数:
    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

 

注意这里使用了 and-or 技巧的简单形式,它是没问题的,因为 lambda 函数在
布尔环境中总是为真。(这并不意味这 lambda 函数不能返回假值。这个函数对
象的布尔值为真;它的返回值可以是任何东西。)
还要注意的是使用了没有参数的 split 函数。你已经看到过它带一个或者两个
参数的使用,但是不带参数它按空白进行分割。
Example 4.21. spl it  不带参数
>>> s = "this   is\na\ttest"  (1)
>>> print s
this   is
a test
>>> print s.split()           (2)
['this', 'is', 'a', 'test']
>>> print " ".join(s.split()) (3)
'this is a test'
(1)  这是一个多行字符串,通过使用转义字符的定义代替了三重引号。\n 是一个回车,\t 是一个制表符。

(2)  不带参数的 split 按照空白进行分割。所以三个空格、一个回车和一个制表符都是一样的。
(3)  通过 split 分割字符串你可以将空格统一化;然后再以单个空格作为分隔符用 join 将其重新连接起来。这也就是 info 函数将多行 doc string 合并成单行所做的事情。
那么 info 函数到底用这些 lambda 函数、split 函数和 and-or 技巧做了些什么呢?
    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

posted on 2009-12-27 16:57  apan  阅读(414)  评论(0编辑  收藏  举报

导航