首页 > 深入Python > 面向对象框架 > UserDict: 一个封装类 | << >> |
diveintopython.org |
|
Python for experienced programmers |
如你所见, FileInfo 是一个象字典一样动作的类。为了进一步揭示这一点,让我们看一看在 UserDict 模块中的 UserDict 类,它是我们的 FileInfo 类的父类。没有什么特别的,类是用Python写的,并且保存在一个 .py 文件里,就象我们的代码。特别的,它保存在你的Python安装目录的 lib 目录下。
在Windows下的Python IDE中,你可以快速打开在你的库路径中的任何模块,使用 File->Locate… (Ctrl-L)。 |
在Python中,你不能子类化象字符串,列表和字典的内置数据类型。作为补偿,Python附带了封装类,可以模拟这些内置的数据类型的行为: UserString,UserList,和 UserDict。通过使用一个由普通和专用方法组成的联合体, UserDict 确实是一个字典的出色模仿品,但是它仅仅是一个类,同任何其它类一样,所以你可以对它进行子类化,以提供可定制的象字典的类,如 FileInfo。
class UserDict: def __init__(self, dict=None): self.data = {} if dict is not None: self.update(dict)
注意 UserDict 是一个基类,不是从任何其他类继承而来。 | |
这就是我们在 FileInfo 类中进行了覆盖的 __init__ 方法。注意这个父类的参数列表与子类不同。某些语言(如Powerbuilder)支持通过参数列表的函数重载,也就是,同名的多个函数,有着不同个数的参数,或不同类型的参数。另外的语言(最明显如PL/SQL)甚至支持通过参数名的重载,也就是,同名的多个函数,有相同个数相同类型的参数,但是参数名字不同。Python不支持以上任何一种方式,不管怎么样它都没有函数重载的形式。一个 __init__ 方法就是一个 __init__ 方法,不管它有什么样的参数。每个类只能有一个 __init__ 方法,并且如果一个子类拥有一个 __init__ 方法,它总是覆盖父类的 __init__ 方法,甚至子类可以用不同的参数列表来定义它。 | |
Python支持数据属性(在PoserBuilder中叫做“实例变量”,在C++中叫“数据成员”),它是由某个特定的类实例所拥有的数据。在本例中,每个 UserDict 实例将拥有一个 data 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,instance.data,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使用 self 作为限定符。为了方便,所有的数据属性都在 __init__ 方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,象局部变量一样,当你首次赋给它值的时候突然产生。 | |
这个语法你可能以前没看过(我还没有在这本书中的例子中用过它)。这是一条 if 语句,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就象在C++中没有用大括号包括的单行语句。)你可以用这种语法,或者可以在后面的行拥有缩近代码,但是不能对同一个块同时用两种方式。 |
应该总是在 __init__ 方法中给所有类的数据属性赋予一个初始值。这样做将会节省你在后面调试的时间。 |
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 是一个普通的类方法,Python从不会替你调用,它只是象类的一般方法一样可用,你可以在任何时候调用它。注意, clear 象所有的类方法一样(正常或专用的),使用 self 作为它的第一个参数,但是当你调用方法时,不用包括 self ;这件事是Python为你加上的。我知道这个有些迷惑人,但是你会完全习惯它的。还应注意这个封装类的基本技术:保存一个真正的字典作为数据属性,定义所有真正字典有拥有的方法,并且将每个类方法重定向到真正字典上的相应方法。(一旦你已经忘记了,字典的 clear 方法删除它的所有关键字和关键字相应的值。) |
|
真正字典的 copy 方法会返回一个新的字典,它是原始字典的原样的复制(所有的键-值对都相同)。但是 UserDict 不能简单地重定向到 self.data.copy,因为那个方法返回一个真正的字典,而我们想要的是返回同一个类的一个新的实例,就象是 self。 |
|
我们使用 __class__ 属性来查看是否 self 是一个 UserDict,如果是,太好了,因为我们知道如何拷贝一个 UserDict:只要创建一个新的 UserDict ,并传给它真正的字典,这个字典已经存放在 self.data中了。 | |
如果 self.__class__ 不是 UserDict,那么 self 一定是 UserDict 的某个子类(如可能为 FileInfo),生活总是存在意外。 UserDict 不知道如何生成它的子类的一个原样的拷贝,例如,有可能在子类中定义了其它的数据属性,所以我们只能完全复制它们,确定拷贝了它们的全部内容。幸运的是,Python带了一个模块可以正确地完成这件事,它叫做 copy。在这里我不想深入细节(然而它是一个绝对酷的模块,是否你想到要自已研究它了呢?)。说 copy 能够拷贝任何Python对象就够了,这就是为什么我们在这里用它的原因。 |
|
其余的方法是直截了当的重定向到 self.data 的内置函数上。 |
进一步阅读 |
|
« 类的实例化 | 专用类方法 » |