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 支持数据属性 (在 JavaPowerbuilder 中叫做 “实例变量”,在 C++ 中叫 “数据成员”),它是由某个特定的类实例所拥有的数据。在本例中,每个 UserDict 实例将拥有一个 data 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,instance.data,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使用 self 作为限定符。习惯上,所有的数据属性都在 __init__ 方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,像局部变量一样,当你首次赋给它值的时候突然产生
update 方法是一个字典复制器:它把一个字典中的键和值全部拷贝到另一个字典。这个操作并不 事先清空目标字典,如果一些键在目标字典中已经存在,则它们将被覆盖,那些键名在目标字典中不存在的则不改变。应该把 update 看作是合并函数,而不是复制函数。
这个语法你可能以前没看过 (我还没有在这本书中的例子中用过它)。这是一条 if 语句,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就像在 C++ 中没有用大括号包括的单行语句。) 你可以用这种语法,或者可以在后面的行写下缩近代码,但是不能对同一个块同时用两种方式。
JavaPowerbuilder 支持通过参数列表的重载,也就是 一个类可以有同名的多个方法,但这些方法或者是参数个数不同,或者是参数的类型不同。其它语言 (最明显如 PL/SQL) 甚至支持通过参数名的重载,也就是 一个类可以有同名的多个方法,这些方法有相同类型,相同个数的参数,但参数名不同。Python 两种都不支持,总之是没有任何形式的函数重载。一个 __init__ 方法就是一个 __init__ 方法,不管它有什么样的参数。每个类只能有一个 __init__ 方法,并且如果一个子类拥有一个 __init__ 方法,它总是 覆盖父类的 __init__ 方法,甚至子类可以用不同的参数列表来定义它。
Python 的原作者 Guido 是这样解释方法覆盖的:“子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,父类中的一个方法在调用同类中的另一方法时,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。 (C++ 程序员可能会这样想:所有的 Python 方法都是虚函数。)”如果你不明白 (它令我颇感困惑),不必在意。我想我要跳过它。[3]
应该总是在 __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 提供封装类来模拟内建数据类型的行为,比如:UserStringUserListUserDict。通过混合使用普通和特殊方法,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 并不这样工作,它不是一个封装所以不需要明确的初始化。

进一步阅读

Footnotes

[3] 实际上,这一点并不是那么难以理解。考虑两个类,basechildbase 中的方法 a 需要调用 self.b;而我们又在 child 中覆盖了方法 b。然后我们创建一个 child 的实例,ch。调用 ch.a,那么此时的方法 a 调用的 b 函数将不是 base.b,而是 child.b。――译注

内蒙古时时彩11选5遗漏数据 时时彩霸主破解 江西时时彩赚钱计划 拉菲2 手机好用的时时彩软件
北斗星时时彩计划软件 重庆时时彩购买技巧 领航时时彩重庆版下载 新疆时时彩走势图彩经网 时时彩走势图sscptzst
时时彩缩水软件免费版 重庆时时彩开奖号码 江西时时彩走势图hg0088gw88 重庆时时彩什么团队好 重庆时时彩暂停销售
时时彩技巧 时时彩奇妙3.1注册机 91shishicai.cc 时时彩组怎么买豹子 金尊时时彩平台网站