楼主: 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
71#
 楼主| 发表于 2006-7-15 19:31 | 只看该作者
5.5. 探索 UserDict: 一个封装类
如你所见, FileInfo 是一个象字典一样动作的类。为了进一步揭示这一点,让我们看一看在 UserDict 模块中的 UserDict 类,它是我们的 FileInfo 类的父类。它没有什么特别的,也是用 Python 写的,并且保存在一个 .py 文件里,就象我们其他的代码。特别之处在于,它保存在你的 Python 安装目录的 lib 目录下。


在 Windows 下的 ActivePython IDE 中,你可以快速打开在你的库路径中的任何模块,使用 File->Locate... (Ctrl-L)。

例 5.9. 定义 UserDict 类

class UserDict:                                
    def __init__(self, dict=None):            
        self.data = {}                        
        if dict is not None: self.update(dict)  
  注意 UserDict 是一个基类,不是从任何其他类继承而来。
  这就是我们 在 FileInfo 类中进行了覆盖 的 __init__ 方法。注意这个父类的参数列表与子类不同。很好,每个子类可以拥有自已的参数集,只要使用正确的参数调用父类就可以了。这里父类有一个定义初始值的方法(通过在 dict 参数中传入一个字典),这一方法我们的 FileInfo 没有用上。
  Python 支持数据属性(在 Java 和 Powerbuilder 中叫做 “实例变量”,在 C++ 中叫 “数据成员”),它是由某个特定的类实例所拥有的数据。在本例中,每个 UserDict 实例将拥有一个 data 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,instance.data,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使用 self 作为限定符。习惯上,所有的数据属性都在 __init__ 方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,象局部变量一样,当你首次赋给它值的时候突然产生。
  update 方法是一个字典复制器:它把一个字典中的键和值全部拷贝到另一个字典。 这个操作 并不 事先清空目标字典,如果一些键在目标字典中已经存在,则它们将被覆盖,那些键名在目标字典中不存在的则不改变。应该把 update 看作是合并函数,而不是复制函数。
  这个语法你可能以前没看过(我还没有在这本书中的例子中用过它)。这是一条 if 语句,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就象在 C++ 中没有用大括号包括的单行语句。) 你可以用这种语法,或者可以在后面的行拥有缩近代码,但是不能对同一个块同时用两种方式。


Java 和 Powerbuilder 支持通过参数列表的重载,也就是 一个类可以有同名的多个方法,但这些方法或者是参数个数不同,或参数的类型不同。其它语言(最明显如 PL/SQL)甚至支持通过参数名的重载,也就是 一个类可以有同名的多个方法,这些方法有相同类型,相同个数的参数,但参数名不同。Python 两种都不支持,总之是没有任何形式的函数重载。一个 __init__ 方法就是一个 __init__ 方法,不管它有什么样的参数。每个类只能有一个 __init__ 方法,并且如果一个子类拥有一个 __init__ 方法,它 总是 覆盖父类的 __init__ 方法,甚至子类可以用不同的参数列表来定义它。

Python 的原作者 Guido 是这样解释方法覆盖的 “子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,在调用同一对象的另外方法时,父类中一个方法对另一个同类中的方法的调用,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。(对于 C++ 程序员,所有的 Python 方法都非常有效)” 如果你不明白(它另我颇感困惑),不必在意。我想我要跳过它。

应该总是在 __init__ 方法中给一个实例的所有数据属性赋予一个初始值。这样做将会节省你在后面调试的时间,不必为捕捉因使用未初始化(也就是不存在)的属性而导致的 AttributeError 异常费时费力。

例 5.10. UserDict 常规方法
    def clear(self): self.data.clear()         
    def copy(self):                             
        if self.__class__ is UserDict:         
            return UserDict(self.data)         
        import copy                             
        return copy.copy(self)                 
    def keys(self): return self.data.keys()     
    def items(self): return self.data.items()  
    def values(self): return self.data.values()
  clear 是一个普通的类方法,可以在任何时候被任何人公开调用。注意,clear 象所有的类方法一样(常规的或专用的),使用 self 作为它的第一个参数。(记住,当你调用方法时,不用包括 self;这件事是 Python 替你做的。) 还应注意这个封装类的基本技术:将一个真正的字典 (data) 作为数据属性保存起来,定义所有真正字典所拥有的方法,并且将每个类方法重定向到真正字典上的相应方法。(在你忘记的情况下,字典的 clear 方法 删除它的所有关键字 和关键字相应的值。)
  真正字典的 copy 方法会返回一个新的字典,它是原始字典的原样的复制(所有的键-值对都相同)。但是 UserDict 不能简单地重定向到 self.data.copy,因为那个方法返回一个真正的字典,而我们想要的是返回同一个类的一个新的实例,就象是 self。
  我们使用 __class__ 属性来查看是否 self 是一个 UserDict,如果是,太好了,因为我们知道如何拷贝一个 UserDict:只要创建一个新的 UserDict ,并传给它真正的字典,这个字典已经存放在 self.data 中了。 然后你立即返回这个新的 UserDict,你甚至于不需要再下面一行中使用 import copy。
  如果 self.__class__ 不是 UserDict,那么 self 一定是 UserDict 的某个子类(如可能为 FileInfo),生活总是存在意外。 UserDict 不知道如何生成它的子类的一个原样的拷贝,例如,有可能在子类中定义了其它的数据属性,所以我们只能完全复制它们,确定拷贝了它们的全部内容。幸运的是,Python 带了一个模块可以正确地完成这件事,它叫做 copy。在这里我不想深入细节(然而它是一个绝对酷的模块,是否你想到要自已研究它了呢?)。说 copy 能够拷贝任何 Python 对象就够了,这就是为什么我们在这里用它的原因。
  其余的方法是直截了当的重定向到 self.data 的内置函数上。


在 Python 2.2 之前的版本中,你不可以直接子类化字符串、列表以及字典之类的内建数据类型。 作为补偿, Python 提供封装类来模拟内建数据类型的行为,比如:UserString, UserList 和 UserDict。 通过混合使用普通和特殊方法, UserDict 类出色于模仿字典。 在 Python 2.2 和其后的版本中,你可以直接从 dict 内建数据类型继承。本书 fileinfo_fromdict.py 中有这方面的一个例子。

如例子中所示,在 Python 中,你可以直接继承自内建数据类型 dict,这样做有三点与 UserDict 不同。

例 5.11. 直接继承自内建数据类型 dict

class FileInfo(dict):                  
    "store file metadata"
    def __init__(self, filename=None):
        self["name"] = filename
  第一个区别是你不需要导入 UserDict 模块,因为 dict 是已经可以使用的内建数据类型。第二个区别是你不是继承自 UserDict.UserDict ,而是直接继承自 dict。
  第三个区别有些晦涩,但却很重要。 UserDict 内部的工作方式要求你手工地调用它的 __init__ 方法去正确初始化它的内部数据结构。 dict 并不这样工作,它不是一个封装所以不需要明确的初始化。

使用道具 举报

回复
论坛徽章:
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
72#
 楼主| 发表于 2006-7-15 19:31 | 只看该作者
进一步阅读

使用道具 举报

回复
论坛徽章:
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
73#
 楼主| 发表于 2006-7-15 19:31 | 只看该作者
5.6. 专用类方法
5.6.1. 获得和设置数据项
除了普通的类方法,还有一些对于 Python 类可以定义的专用方法。专用方法是在特殊情况下或当使用特别语法时由 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
74#
 楼主| 发表于 2006-7-15 19:32 | 只看该作者
5.6.1. 获得和设置数据项
例 5.12. __getitem__ 专用方法
    def __getitem__(self, key): return self.data[key]>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3"
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__getitem__("name"
'/music/_singles/kairo.mp3'
>>> f["name"]            
'/music/_singles/kairo.mp3'  __getitem__ 专用方法很简单。象普通的方法 clear,keys 和 values 一样,它只是重定向到字典,返回字典的值。但是怎么调用它呢?哦,你可以直接调用 __getitem__,但是在实际中你其实不会那样做:我在这里执行它只是要告诉你它是如何工作的。正确地使用 __getitem__ 的方法是让 Python 来替你调用。
  这个看上去就象你用来 得到一个字典值 的语法,事实上它返回你期望的值。下面是隐藏起来的一个环节:暗地里,Python 已经将这个语法转化为 f.__getitem__("name" 的方法调用。这就是为什么 __getitem__ 是一个专用类方法的原因,不仅仅是你可以自已调用它,还可以通过使用正确的语法让 Python 来替你调用。

当然, Python 有一个与 __getitem__ 类似的 __setitem__ 专用方法, 参见下面的例子。

例 5.13. __setitem__ 专用方法
    def __setitem__(self, key, item): self.data[key] = item>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__setitem__("genre", 31)
>>> f
{'name':'/music/_singles/kairo.mp3', 'genre':31}
>>> f["genre"] = 32            
>>> f
{'name':'/music/_singles/kairo.mp3', 'genre':32}  与 __getitem__ 方法一样,__setitem__ 简单地重定向到真正的字典 self.data ,让它来进行工作。并且象 __getitem__ 一样,通常你不会直接调用它,当你使用了正确的语法,Python 会替你调用 __setitem__ 。
  这个看上去象正常的字典语法,当然除了 f 实际上是一个类,它尽可能地打扮成一个字典,并且 __setitem__ 是打扮的一个重点。这行代码实际上暗地里调用了 f.__setitem__("genre", 32)。

__setitem__ 是一个专用类方法,因为它可以让 Python 来替你调用,但是它仍然是一个类方法。就象在 UserDict 中定义 __setitem__ 方法一样容易,我们可以在子类中重新定义它,对父类的方法进行覆盖。这就允许我们定义出在某些方面象字典一样动作的类,但是可以定义它自已的行为,超过和超出内置的字典。

这个概念是本章中我们正在学习的整个框架的基础。每个文件类型可以拥有一个处理器类,这些类知道如何从一个特殊的文类型得到元数据。一但知道了某些属性(象文件名和位置),处理器类就知道如何自动地得到其它的属性。它的实现是通过覆盖 __setitem__ 方法,检查特别的关键字,然后当找到后加入额外的处理。

例如,MP3FileInfo 是 FileInfo 的子类。在设置了一个 MP3FileInfo 类的 name 时,并不只是设置 name 关键字(象父类 FileInfo 所做的),它还要在文件自身内进行搜索 MP3 的标记然后填充一整套关键字集合。 下面的例子将展示其如何工作。

使用道具 举报

回复
论坛徽章:
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
75#
 楼主| 发表于 2006-7-15 19:32 | 只看该作者
例 5.14. 在 MP3FileInfo 中覆盖 __setitem__
    def __setitem__(self, key, item):         
        if key == "name" and item:            
            self.__parse(item)               
        FileInfo.__setitem__(self, key, item)   注意我们的 __setitem__ 方法严格按照父类方法相同的形式进行定义。这一点很重要,因为 Python 将替你执行方法,则它希望这个函数用确定个数的参数进行定义。 (从技术上说,参数的名字没有关系,只是个数。)
  这里就是整个 MP3FileInfo 类的难点:如果给 name 关键字赋一个值,我们还想做些额外的事情。
  我们对 name 所做的额外处理封装在了 __parse 方法中。这是定义在 MP3FileInfo 中的另一个类方法,则当我们调用它时,我们用 self 对其限定。仅是调用 __parse 将只会看成定义在类外的普通方法,调用 self.__parse 将会看成定义在类中的一个类方法。这不是什么新东西,你用同样的方法来引用 数据属性。
  在做完我们额外的处理之后,我们需要调用父类的方法。记住,在 Python 中不会自动为你完成,需手工执行。注意,我们在调用直接父类, FileInfo,尽管它没有一个 __setitem__ 方法。没问题,因为 Python 将会沿着父类树走,直到它找到一个有着我们正在调用方法的类,所以这行代码最终会找到并且调用定义在 UserDict 中的 __setitem__。


当在一个类中存取数据属性时,你需要限定属性名:self.attribute。当调用类中的其它方法时,你属要限定方法名:self.method。

例 5.15. 设置 MP3FileInfo 的 name
>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo()                  
>>> mp3file
{'name':None}
>>> mp3file["name"] = "/music/_singles/kairo.mp3"      
>>> mp3file
{'album': 'Rave Mix', 'artist': '***DJ MARY-JANE***', 'genre': 31,
'title': 'KAIRO****THE BEST GOA', 'name': '/music/_singles/kairo.mp3',
'year': '2000', 'comment': 'http://mp3.com/DJMARYJANE'}
>>> mp3file["name"] = "/music/_singles/sidewinder.mp3"
>>> mp3file
{'album': '', 'artist': 'The Cynic Project', 'genre': 18, 'title': 'Sidewinder',
'name': '/music/_singles/sidewinder.mp3', 'year': '2000',
'comment': 'http://mp3.com/cynicproject'}  首先,我们创建了一个 MP3FileInfo 的实例,没有传递给它文件名。(我们可以不用它,因为 __init__ 方法的 filename 参数是 可选的。) 因为 MP3FileInfo 没有它自已的 __init__ 方法,Python 沿着父类树走,发现了 FileInfo 的 __init__ 方法。这个 __init__ 方法手工调用了 UserDict 的 __init__ 方法,然后设置 name 关键字为 filename,它为 None,因为我们还没有传入一个文件名。所以,mp3file 最初看上去象是有一个关键字的字典,name 的值为 None。
  现在真正有趣的开始了。设置 mp3file 的 name 关键字触发了 MP3FileInfo 上的 __setitem__ 方法(不是 UserDict),这个方法注意到我们正在用一个真实的值来设置 name 关键字,接着调用 self.__parse。尽管我们完全还没有研究过 __parse 方法,从它的输出你可以看出,它设置了其它几个关键字:album, artist, genre, title, year 和 comment。
  修改 name 关键字将再次经受同样的处理过程:Python 调用 __setitem__,__setitem__调用 self.__parse,self.__parse 设置其它所有的关键字。

使用道具 举报

回复
论坛徽章:
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
76#
 楼主| 发表于 2006-7-15 19:32 | 只看该作者
5.7. 高级专用类方法
除了 __getitem__ 和 __setitem__ 之外 Python 还有更多的专用函数。某些可以让你模拟出你甚至可能不知道的功能。

下面的例子将展示 UserDict 一些其他专用方法。

例 5.16. UserDict 中更多的专用方法
    def __repr__(self): return repr(self.data)     
    def __cmp__(self, dict):                       
        if isinstance(dict, UserDict):            
            return cmp(self.data, dict.data)      
        else:                                    
            return cmp(self.data, dict)           
    def __len__(self): return len(self.data)      
    def __delitem__(self, key): del self.data[key]   __repr__ 是一个专用的方法,在当调用 repr(instance) 时被调用。repr 函数是一个内置函数,它返回一个对象的字符串表示。它可以用在任何对象上,不仅仅是类的实例。你已经对 repr 相当熟悉了,尽管你不知道它。在交互式窗口中,当你只敲入一个变量名,接着按ENTER,Python 使用 repr 来显示变量的值。自已用一些数据来创建一个字典 d ,然后用 print repr(d) 来看一看吧。
  __cmp__ 在比较类实例时被调用。通常,你可以通过使用 == 比较任意两个 Python 对象,不只是类实例。有一些规则,定义了何时内置数据类型被认为是相等的,例如,字典在有着全部相同的关键字和值时是相等的。对于类实例,你可以定义 __cmp__ 方法,自已编写比较逻辑,然后你可以使用 == 来比较你的类,Python 将会替你调用你的 __cmp__ 专用方法。
  __len__ 在调用 len(instance) 时被调用。len 是一个内置函数,可以返回一个对象的长度。它可以用于任何被认为理应有长度的对象。字符串的 len 是它的字符个数;字典的 len 是它的关键字的个数;列表或序列的 len 是元素的个数。对于类实例,定义 __len__ 方法,接着自已编写长度的计算,然后调用 len(instance),Python 将替你调用你的 __len__ 专用方法。
  __delitem__ 在调用 del instance[key] 时调用 ,你可能记得它作为 从字典中删除单个元素 的方法。当你在类实例中使用 del 时,Python 替你调用 __delitem__ 专用方法。


在 Java 中,通过使用 str1 == str2 可以决定两个字符串变量是否指向同一块物理内存位置。这就做 对象同一性,在 Python 中写为 str1 is str2。在 Java 中为了比较两个字符串值,你要使用 str1.equals(str2);在 Python 中,你要使用 str1 == str2。 某些 Java 程序员,他们已经被教授得认为,因为在 Java 中 == 是通过一致性而不是值来进行比较的,所以 Java 是更好的地方。这些人要转到 Python 上来可能要花些时间。在 Java 中,你通过 str1 == str2 来比较两个字符串是否指向同一物理内存位置。这被称作 对象确定(object identity), 在 Python 中被写作 str1 is str2。 在 Java 中比较字符串使用 str1.equals(str2) ,而在 Python 中,你使用 str1 == str2。那些素来认为 Java 中比较对象身份而不是值的 == 令世界变得更好的 Java 程序员可能对 Python 中的“这个缺少”接受起来有困难。

在这个地方,你可能会想,“所有这些工作只是为了在类中做一些我可以对一个内置数据类型所做的操作”。不错,如果你能够从象字典一样的内置数据类型进行继承的话,事情就容易多了(那样整个 UserDict 类将完全不需要了)。尽管你可以这样做,专用方法仍然是有用的,因为它们可以用于任何的类,而不只是象 UserDict 这样的封装类。

专用方法意味着 任何类 可以象字典一样保存键-值对,只要定义 __setitem__ 方法。任何类可以表现得象一个序列,只要通过定义 __getitem__ 方法。任何定义了 __cmp__ 方法的类可以用 == 进行比较。并且如果你的类表现拥有类似长度的东西,不要定义 GetLength 方法,而定义 __len__ 方法,使用 len(instance)。


其它的面向对象语言仅让你定义一个对象的物理模型(“这个对象有 GetLength 方法”),而 Python 的专用类方法象 __len__ 允许你定义一个对象的逻辑模型(“这个对象有一个长度”)。  

Python 存在许多其它的专用方法。有一整套的专用方法,可以让类表现得象数值一样,允许你在类实例上进行加,减,和执行其它算数操作。(关于这一点典型的例子就是表示复数的类,数值带有实数和虚数部分。) __call__ 方法让一个类表现得象一个函数,允许你直接调用一个类实例。并且存在其它的专用函数,允许类拥有只读或只写数据属性,在后面的章节中我们会更多地谈到这些。

使用道具 举报

回复
论坛徽章:
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
77#
 楼主| 发表于 2006-7-15 19:32 | 只看该作者
进一步阅读

使用道具 举报

回复
论坛徽章:
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
78#
 楼主| 发表于 2006-7-15 19:33 | 只看该作者
5.8. 类属性介绍
你已经知道了 数据属性,它们是被一个特定的类实例所拥有的变量。Python 也支持类属性,它们是由类本身所拥有的。

例 5.17. 类属性介绍

class MP3FileInfo(FileInfo):
    "store ID3v1.0 MP3 tags"
    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}>>> import fileinfo
>>> fileinfo.MP3FileInfo            
<class fileinfo.MP3FileInfo at 01257FDC>
>>> fileinfo.MP3FileInfo.tagDataMap
{'title': (3, 33, <function stripnulls at 0260C8D4>,
'genre': (127, 128, <built-in function ord>,
'artist': (33, 63, <function stripnulls at 0260C8D4>,
'year': (93, 97, <function stripnulls at 0260C8D4>,
'comment': (97, 126, <function stripnulls at 0260C8D4>,
'album': (63, 93, <function stripnulls at 0260C8D4>}
>>> m = fileinfo.MP3FileInfo()      
>>> m.tagDataMap
{'title': (3, 33, <function stripnulls at 0260C8D4>,
'genre': (127, 128, <built-in function ord>,
'artist': (33, 63, <function stripnulls at 0260C8D4>,
'year': (93, 97, <function stripnulls at 0260C8D4>,
'comment': (97, 126, <function stripnulls at 0260C8D4>),
'album': (63, 93, <function stripnulls at 0260C8D4>)}  MP3FileInfo 是类本身,不是任何类的特别实例。
  tagDataMap 是一个类属性:字面的意思,一个类的属性。它是在创建任何类实例之前就有效了。
  类属性既可以通过直接对类的引用,也可以通过对类的任意实例的引用来使用。


在 Java 中,静态变量(在 Python 中叫类属性)和实例变量(在 Python 中叫数据属性)两者是紧跟在类定义之后定义的(一个有 static 关键字,一个没有)。在 Python 中,只有类属性可以定义在这里,数据属性定义在 __init__ 方法中。

类属性可以作为类级别的常量来使用(这就是为什么我们在 MP3FileInfo 中使用它们),但是它们不是真正的常量。你也可以修改它们。


在 Python 中没有常量。如果你试图努力的话什么都可以改变。这一点满足 Python 的核心原则之一:坏的行为应该被克服而不是被取缔。如果你真正想改变 None 的值,也可以做到,但当无法调试的时候别来找我。

例 5.18. 修改类属性
>>> class counter:
...     count = 0                     
...     def __init__(self):
...         self.__class__.count += 1
...     
>>> counter
<class __main__.counter at 010EAECC>
>>> counter.count                     
0
>>> c = counter()
>>> c.count                           
1
>>> counter.count
1
>>> d = counter()                     
>>> d.count
2
>>> c.count
2
>>> counter.count
2  count 是 counter 类的一个类属性。
  __class__ 是每个类实例的一个内置属性(也是每个类的)。它是一个类的引用,而 self 是一个类(在本例中, 是 counter 类)的实例。
  因为 count 是一个类属性,它可以在我们创建任何类实例之前,通过直接对类引用而得到。
  创建一个类实例会调用 __init__ 方法,它会给类属性 count 加 1。这样会影响到类自身,不只是新创建的实例。
  创建第二个实例将再次增加类属性 count。注意类属性是如何被类和所有类实例所共享的。

使用道具 举报

回复
论坛徽章:
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
79#
 楼主| 发表于 2006-7-15 19:38 | 只看该作者
5.9. 私有函数
与大多数语言一样,Python 也有私有的概念:

私有函数不可以从它们的模块外面被调用
私有类方法不能够从它们的类外面被调用
私有属性不能够从它们的类外面被访问
与大多数的语言不同,一个 Python 函数,方法,或属性是私有还是公有,完全取决于它的名字。

如果一个 Python 函数,类方法,或属性的名字以两个下划线开始(但不是结束),它是私有的;其它所有的都是公有的。 Python 没有类方法 保护 的概念(只能用于它们自已的类和子类中)。类方法或者是私有(只能在它们自已的类中使用)或者是公有(任何地方都可使用)。

在 MP3FileInfo 中,有两个方法:__parse 和 __setitem__。正如我们已经讨论过的,__setitem__ 是一个 专有方法;通常,你不直接调用它,而是通过在一个类上使用字典语法来调用,但它是公有的,并且如果有一个真正好的理由,你可以直接调用它(甚至从 fileinfo 模块的外面)。然而,__parse 是私有的,因为在它的名字前面有两个下划线。


在 Python 中,所有的专用方法(象 __setitem__)和内置属性(象 __doc__)遵守一个标准的命名习惯:开始和结束都有两个下划下。不要对你自已的方法和属性用这种方法命名;到后面,它只会把你(或其它人)搞乱。

使用道具 举报

回复
论坛徽章:
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
80#
 楼主| 发表于 2006-7-15 19:38 | 只看该作者
例 5.19. 尝试调用一个私有方法
>>> import fileinfo
>>> m = fileinfo.MP3FileInfo()
>>> m.__parse("/music/_singles/kairo.mp3"
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'MP3FileInfo' instance has no attribute '__parse'  如果你试图调用一个私有方法,Python 将引发一个有些误导的异常,宣称那个方法不存在。当然它确实存在,但是它是私有的,所以在类外是不可使用的。严格地说,私有方法在它们的类外是可以访问的,只是不 容易 处理。在 Python 中没有什么是真正私有的;在内部,私有方法和属性的名字被忽然改变和恢复,以致于使得它们看上去用它们给定的名字是无法使用的。你可以通过 _MP3FileInfo__parse 名字来使用 MP3FileInfo 类的 __parse 方法。 知道了这个方法很有趣,然后要保证决不在真正的代码中使用它。私有方法由于某种原因而私有,但是象其它很多在 Python 中的东西一样,它们的私有化基本上是习惯问题,而不是强迫的。

使用道具 举报

回复

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

本版积分规则 发表回复

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