楼主: lastwinner

[参考文档] Python 研究(Dive Into Python)

[复制链接]
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
141#
 楼主| 发表于 2006-7-15 20:05 | 只看该作者
例 8.8. BaseHTMLProcessor 介绍

class BaseHTMLProcessor(SGMLParser):
    def reset(self):                        
        self.pieces = []
        SGMLParser.reset(self)

    def unknown_starttag(self, tag, attrs):
        strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs])
        self.pieces.append("<%(tag)s%(strattrs)s>" % locals())

    def unknown_endtag(self, tag):         
        self.pieces.append("</%(tag)s>" % locals())

    def handle_charref(self, ref):         
        self.pieces.append("&#%(ref)s;" % locals())

    def handle_entityref(self, ref):        
        self.pieces.append("&%(ref)s" % locals())
        if htmlentitydefs.entitydefs.has_key(ref):
            self.pieces.append(";"

    def handle_data(self, text):            
        self.pieces.append(text)

    def handle_comment(self, text):         
        self.pieces.append("<!--%(text)s-->" % locals())

    def handle_pi(self, text):              
        self.pieces.append("<?%(text)s>" % locals())

    def handle_decl(self, text):
        self.pieces.append("<!%(text)s>" % locals())  reset 由 SGMLParser.__init__ 来调用。在调用父类方法之前将 self.pieces 初始化为空列表。self.pieces 是一个 数据属性,将用来保存将要构造的 HTML 文档的片段。每个处理器方法都将重构 SGMLParser 所分析出来的 HTML,并且每个方法将生成的字符串追加到 self.pieces 之后。注意,self.pieces 是一个 list。也许您想将它定义为一个字符串,然后不停地将每个片段追加到它的后面。这样做是可以的,但是 Python 在处理 list 方面效率更高一些。 [2]  
  因为 BaseHTMLProcessor 没有为特别标记定义方法 (如在 URLLister 中的start_a 方法), SGMLParser 将对每一个开始标记调用 unknown_starttag 方法。这个方法接收标记 (tag) 和属性的名字/值对的 list(attrs) 两参数,重新构造初始的 HTML,接着将结果追加到 self.pieces 后。 这里的 字符串格式化 有些陌生,我们将留到下一节再说明。
  重构结束标记要简单得多,只是使用标记名字,把它包在 </...> 括号中。
  当 SGMLParser 找到一个字符引用时,会用原始的引用来调用 handle_charref。如果 HTML 文档包含   这个引用,ref 将为 160。重构原始的完整的字符引用只要将 ref 包装在 &#...; 字符中间。
  实体引用同字符引用相似,但是没有#号。重建原始的实体引用只要将 ref 包装在 &...; 字符串中间。 (实际上,一位博学的读者曾经向我指出,除些之外还稍微有些复杂。仅有某种标准的 HTML 实体以一个分号结束;其它看上去差不多的实体并不如此。幸运的是,标准 HTML 实体集已经定义在 Python 的一个叫做 htmlentitydefs 的模块中了。从而引出额外的 if 语句。)  
  文本块则简单地不经修改地追加到 self.pieces 后。
  HTML 注释包装在 <!--...--> 字符中。
  处理指令包装在 <?...> 字符中。


HTML 规范要求所有非 HTML (象客户端的 JavaScript) 必须包括在 HTML 注释中,但不是所有的页面都是这么做的 (而且所有的最新的浏览器也都容许不这样做) 。BaseHTMLProcessor 不允许这样,如果脚本嵌入的不正确,它将被当作 HTML 一样进行分析。例如,如果脚本包含了小于和等于号,SGMLParser 可能会错误地认为找到了标记和属性。SGMLParser 总是把标记名和属性名转换成小写,这样可能破坏了脚本,并且 BaseHTMLProcessor 总是用双引号来将属性封闭起来 (尽管原始的 HTML 文档可能使用单引号或没有引号) ,这样必然会破坏脚本。应该总是将您的客户端脚本放在 HTML 注释中进行保护。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
142#
 楼主| 发表于 2006-7-15 20:05 | 只看该作者
例 8.9. BaseHTMLProcessor 输出结果
    def output(self):               
        """Return processed HTML as a single string"""
        return "".join(self.pieces)   这是在 BaseHTMLProcessor 中的一个方法,它永远不会被父类 SGMLParser 所调用。因为其它的处理器方法将它们重构的 HTML 保存在 self.pieces 中,这个函数需要将所有这些片段连接成一个字符串。正如前面提到的,Python 在处理列表方面非常出色,但对于字符串处理就逊色了。所以我们只有在某人确实需要它时才创建完整的字符串。
  如果您愿意,也可以换成使用 string 模块的 join 方法: string.join(self.pieces, ""

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
143#
 楼主| 发表于 2006-7-15 20:06 | 只看该作者
进一步阅读

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
144#
 楼主| 发表于 2006-7-15 20:06 | 只看该作者
Footnotes
[2] Python 处理 list 比字符串快的原因是: list 是可变的,但字符串是不可变的。这就是说向 list 进行追加只是增加元素和修改索引。因为字符串在创建之后不能被修改,象 s = s + newpiece 这样的代码将会从原值和新片段的连接结果中创建一个全新的字符串,然后丢弃原来的字符串。这样就需要大量昂贵的内存管理,并且随着字符串变长,所需要的开销也在增长。所以在一个循环中执行 s = s + newpiece 非常不好。用技术术语来说,向一个 list 追加 n 个项的代价为 O(n),而向一个字符串追加 n 个项的代价是 O(n2)。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
145#
 楼主| 发表于 2006-7-15 20:07 | 只看该作者
8.5. locals 和 globals
我们先偏离一下 HTML 处理的主题, 讨论一下 Python 如何处理变量。 Python 有两个内置的函数, locals 和 globals, 它们提供了基于 dictionary 的访问局部和全局变量的方式。
还记得 locals 吗? 您第一次是在这里看到的:
    def unknown_starttag(self, tag, attrs):
        strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs])
        self.pieces.append("" % locals())
不, 等等, 此时您还不能理解 locals 。首先, 您需要学习关于命名空间的知识。这很枯燥, 但是很重要, 因此要要耐心些。
Python 使用叫做名字空间的东西来记录变量的轨迹。名字空间只是一个 dictionary ,它的键字就是变量名,它的值就是那些变量的值。实际上,名字空间可以象 Python 的 dictionary 一样进行访问,一会我们就会看到。
在一个 Python 程序中的任何一个地方,都存在几个可用的名字空间。每个函数都有着自已的名字空间,叫做局部名字空间,它记录了函数的变量,包括函数的参数和局部定义的变量。每个模块拥有它自已的名字空间,叫做全局名字空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。还有就是内置名字空间,任何模块均可访问它,它存放着内置的函数和异常。
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
  • 局部名字空间 - 特指当前函数或类的方法。如果函数定义了一个局部变量 x, 或一个参数 x,Python 将使用它,然后停止搜索。
  • 全局名字空间 - 特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
  • 内置名字空间 - 对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
146#
 楼主| 发表于 2006-7-15 20:07 | 只看该作者
如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,同时传 递 There is no variable named 'x' 这样一条信息,回到 例 3.18 “引用未赋值的变量”,您会看到一路上都有这样的信息。但是您并没有体会到 Python 在给出这样的错误之前做了多少的努力。


Python 2.2 引入了一种略有不同但重要的改变,它会影响名字空间的搜索顺序: 嵌套的作用域。 在 Python 2.2 版本之前,当您在一个嵌套函数 或 lambda 函数 中引用一个变量时,Python 会在当前 (嵌套的或 lambda) 函数的名字空间中搜索,然后在模块的名字空间。Python 2.2 将只在当前 (嵌套的或 lambda) 函数的名字空间中搜索,然后是在父函数的名字空间中搜索,接着是模块的名字空间中搜索。Python 2.1 可 以两种方式工作,缺省地,按 Python 2.0 的方式工作。但是您可以把下面一行代码增加到您的模块头部,使您的模块工作起来象 Python 2.2 的方式:

from __future__ import nested_scopes

您是否为此而感到困惑? 不要绝望! 我敢说这一点非常酷。象 Python 中的许多事情一样,名字空间 在运行时直接可以访问。怎么样? 不错吧,局部名字空间可以通过内置的 locals 函数来访问。全局 (模块级别) 名字空间可以通过内置的 globals 函数来访问。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
147#
 楼主| 发表于 2006-7-15 20:07 | 只看该作者
例 8.10. locals 介绍
>>> def foo(arg):
...     x = 1
...     print locals()
...     
>>> foo(7)        
{'arg': 7, 'x': 1}
>>> foo('bar')   
{'arg': 'bar', 'x': 1}  函数 foo 在它的局部名字空间中有两个变量: arg,它的值是被传入函数的,和 x, 它是在函数里定义的。
  locals 返回一个名字/值对的 dictionary。这个 dictionary 的键字是字符串形式的变量名字,dictionary 的值是变量的实际值。所以用 7 来调用 foo,会打印出包含函数两个局部变量的 dictionary: arg (7) 和 x (1)。
  回想一下,Python 有动态数据类型,所以您可以非常容易地传递给 arg 一个字符串,这个函数 (和对 locals 的调用) 将仍然很好的工作。locals 可以用于所有类型的变量。

locals 对局部 (函数) 名字空间做了些什么,globals 就对全局 (模块) 名字空间做了什么。然而 globals 更令人兴奋,因为一个模块的名字空间是更令人兴奋的。[3] 不仅仅是模块的名字空间包含了模块级的变量和常量,它还包括了所有在模块中定义的函数和类。再加上,它包括了任何被导入到模块中的东西。

回想一下 from module import 和 import module 之间的不同。使用 import module,模块自身被导入,但是它保持着自已的名字空间,这就是为什么您需要使用模块名来访问它的函数或属性: module.function 的原因。但是使用 from module import,实际上是从另一个模块中将指定的函数和属性导入到您自己的名字空间,这就是为什么您可以直接访问它们却不需要引用它们所来源的模块的原因。使用 globals 函数,您会真切地看到这一切的发生。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
148#
 楼主| 发表于 2006-7-15 20:07 | 只看该作者
例 8.11. globals 介绍
看看下面列出的在文件 BaseHTMLProcessor.py 尾部的代码块:


if __name__ == "__main__":
    for k, v in globals().items():            
        print k, "=", v  不要被吓坏了,想想以前您已经全部都看到过了。globals 函数返回一个 dictionary,我们使用 items 方法和多变量赋值来遍历 dictionary。在这里唯一的新东西就是 globals 函数。

现在从命令行运行这个脚本会得到下面的输出 (注意您的输出可能有略微的不同, 这依赖于您的系统平台和所安装的 Python 版本):

c:\docbook\dip\py> python BaseHTMLProcessor.pySGMLParser = sgmllib.SGMLParser               
htmlentitydefs = <module 'htmlentitydefs' from 'C:\Python23\lib\htmlentitydefs.py'>
BaseHTMLProcessor = __main__.BaseHTMLProcessor
__name__ = __main__                           
...略...  SGMLParser 使用了 from module import 从 sgmllib 中被导入。也就是说它被直接导入到我们的模块名字空间了,就是这样。
  对比这个和 htmlentitydefs, 它是用 import 被导入的。 也就是说 htmlentitydefs 模块本身也在名字空间中, 但是 entitydefs 变量定义在 htmlentitydefs 之外。
  这个模块只定义一个类, BaseHTMLProcessor, 不错。 注意这儿的值就是类本身,不是一个特别的类实例。
  记得 if __name__ 技巧 吗?当运行一个模块时 (对从另外一个模块中导入而言) ,内置的 __name__ 是一个特殊值 __main__。因为我们是把这个模块当作脚本从命令来运行的,故 __name__ 值为 __main__,这就是为什么我们这段简单地打印 globals 的代码可以执行的原因。


使用 locals 和 globals 函数,通过提供变量的字符串名字您可以动态地得到任何变量的值。这种方法提供了这样的功能: getattr 函数允许您通过提供函数的字符串名来动态地访问任意的函数。

在 locals 与 globals 之间有另外一个重要的区别,您应该在它困扰您之前就了解它。它无论如何都会困扰您的,但至少您还记得了解过它。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
149#
 楼主| 发表于 2006-7-15 20:08 | 只看该作者
例 8.12. locals 是只读的, globals 不是

def foo(arg):
    x = 1
    print locals()   
    locals()["x"] = 2
    print "x=",x      

z = 7
print "z=",z
foo(3)
globals()["z"] = 8   
print "z=",z         
  因为使用 3 来调用 foo,会打印出 {'arg': 3, 'x': 1}。这个应该没什么奇怪的。
  locals 是一个返回 dictionary 的函数, 并且在 dictionary 中设置一个值。您可能认为这样会改变局部变量 x 的值为 2,但并不会。locals 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行改变对局部名字空间中的变量值并无影响。
  这样会打印出 x= 1,而不是 x= 2。
  在有了对 locals 的经验之后,您可能认为这样 不会 改变 z 的值,但是可以。由于 Python 在实现过程中内部有所区别 (关于这些区别我宁可不去研究,因为我自已还没有完全理解) ,globals 返回实际的全局名字空间,而不是一个拷贝: 与 locals 的行为完全相反。所以对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量。
  这样会打印出 z= 8,而不是 z= 7。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
150#
 楼主| 发表于 2006-7-15 20:08 | 只看该作者
Footnotes
[3] 我没有说得太多吧。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表