5.7. 高级特殊类方法

Python 拥有的特殊方法不仅仅是 __getitem____setitem__。其中一些方法可以让您模拟您可能根本不知道的功能。

此示例展示了 UserDict 中的其他一些特殊方法。

示例 5.16. UserDict 中的更多特殊方法

    def __repr__(self): return repr(self.data)     1
    def __cmp__(self, dict):                       2
        if isinstance(dict, UserDict):            
            return cmp(self.data, dict.data)      
        else:                                     
            return cmp(self.data, dict)           
    def __len__(self): return len(self.data)       3
    def __delitem__(self, key): del self.data[key] 4
1 __repr__ 是一种特殊方法,当您调用 repr(实例) 时会调用它。repr 函数是一个内置函数,它返回对象的字符串表示形式。它适用于任何对象,而不仅仅是类实例。您已经非常熟悉 repr,即使您可能没有意识到。在交互式窗口中,当您只输入一个变量名并按下 ENTER 键时,Python 会使用 repr 来显示变量的值。尝试创建一个包含一些数据的字典 d,然后使用 print repr(d) 来亲自查看。
2 __cmp__ 在您比较类实例时被调用。通常,您可以使用 == 比较任意两个 Python 对象,而不仅仅是类实例。有一些规则定义了何时认为内置数据类型相等;例如,当字典具有所有相同的键和值时,它们是相等的;当字符串长度相同且包含相同的字符序列时,它们是相等的。对于类实例,您可以定义 __cmp__ 方法并自己编写比较逻辑,然后可以使用 == 比较类的实例,Python 会为您调用 __cmp__ 特殊方法。
3 __len__ 在您调用 len(实例) 时被调用。len 函数是一个内置函数,它返回对象的长度。它适用于任何可以合理地认为具有长度的对象。字符串的 len 是其字符数;字典的 len 是其键数;列表或元组的 len 是其元素数。对于类实例,定义 __len__ 方法并自己编写长度计算代码,然后调用 len(实例)Python 会为您调用 __len__ 特殊方法。
4 __delitem__ 在您调用 del 实例[] 时被调用,您可能还记得这是从字典中删除单个项目的方法。当您在类实例上使用 del 时,Python 会为您调用 __delitem__ 特殊方法。
Note
Java 中,您可以使用 str1 == str2 来确定两个字符串变量是否引用同一个物理内存位置。这被称为对象标识,在 Python 中写为 str1 is str2。要在 Java 中比较字符串值,您将使用 str1.equals(str2);在 Python 中,您将使用 str1 == str2。那些被教导相信世界会更美好,因为 Java 中的 == 是按标识而不是按值进行比较的 Java 程序员,在适应 Python 缺乏这种“陷阱”时可能会遇到困难。

在这一点上,您可能会想:“做了这么多工作,只是为了在一个类中做一些我可以用内置数据类型完成的事情。” 的确,如果您可以从字典之类的内置数据类型继承,生活会更轻松(并且整个 UserDict 类都是不必要的)。但即使您可以这样做,特殊方法仍然很有用,因为它们可以用于任何类,而不仅仅是像 UserDict 这样的包装类。

特殊方法意味着任何类都可以像字典一样存储键/值对,只需定义 __setitem__ 方法即可。任何类都可以像序列一样工作,只需定义 __getitem__ 方法即可。任何定义了 __cmp__ 方法的类都可以使用 == 进行比较。如果您的类表示具有长度的内容,请不要定义 GetLength 方法;定义 __len__ 方法并使用 len(实例)

Note
其他面向对象语言只允许您定义对象的物理模型(“此对象具有 GetLength 方法”),而 Python 的特殊类方法(如 __len__)允许您定义对象的逻辑模型(“此对象具有长度”)。

Python 还有很多其他的特殊方法。有一整套方法可以让类像数字一样工作,允许您对类实例进行加、减和其他算术运算。(这方面的典型例子是一个表示复数的类,即具有实部和虚部的数字。)__call__ 方法可以让一个类像函数一样工作,允许您直接调用一个类实例。还有其他的特殊方法允许类具有只读和只写的数据属性;我们将在后面的章节中详细讨论这些内容。

特殊类方法的扩展阅读