本节书摘来自异步社区《Python面向对象编程指南》一书中的第1章,第1.1节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。
第1部分 用特殊方法实现Python风格的类
- __init__()方法
- 与Python无缝集成——基本特殊方法
- 属性访问、特性和修饰符
- 抽象基类设计的一致性
- 可调用对象和上下文的使用
- 创建容器和集合
- 创建数值类型
- 装饰器和Mixins——横切方面
用特殊方法实现
Python风格的类 通过重写特殊方法来完成对Python内部机制的调用,在Python中是很普遍的。例如len()函数就可以重写一个类的__len__()方法。
这意味着对于像(len(x))这样的通用公共接口,任何类(例如,声明一个类叫tidy)都可以实现它。任何类都可以重写一个像__len()__这样的特殊方法,这样一种机制构成了Python多态机制的一部分;任何实现了__len()__函数的类都会响应通用公共接口(len(x))中的len()函数。
每当定义一个类,可以(而且应该)提供这些特殊方法的实现来与Python语言更好地结合。本书的第1部分“用特殊方法实现Python风格的类”是对传统面向对象设计的一种延伸,可以使创建的Python类更具Python风格。任何一个类都应当与Python语言其余的任何原生部分很好地结合。这样一来,既可以重用很多其他语言现有的功能和标准库,而且编写的包和模块也将更容易维护和扩展。
在某种程度上,创建的类都可以作为Python扩展的形式来实现。开发者都希望自己的类更接近Python语言的原生类。这样一来,在语言之间、标准库之间以及应用程序之间的代码区别就能够最小化。
为了实现更好的可扩展性,Python语言提供了大量的特殊方法,它们大致分为以下几类。
- 特性访问(Attribute Access):这类特殊方法实现了对象的特性访问,使用方式为object.attribute,既可以用来赋值,也可以在del语句中执行删除操作。
- 可调用对象(Callables):这个方法的适用对象为参数,就像Python内部的len ()函数。(也是应用于参数。)
- 集合(Collections):这类方法提供了很多集合操作的功能。类似这类方法的使用有sequence[index]、mapping[key]和some_set | another_set。
- 数字(Numbers):这类方法提供了大量的数学运算符和比较运算符。可以使用这些方法来扩展Python的数字部分。
- 上下文(Context):这类函数通常使用with语句来实现上下文的管理。
- 迭代器(Iterator):可以使用这类方法定义迭代器,通常不需要考虑这部分的扩展,因为生成器(Generator)已经提供了非常优雅的实现。然而,我们仍会探究如何创建自己的迭代器。
在Python 3 Object Oriented Programming一书中已经介绍了这些特殊方法中的一部分,以下我们将重新回顾这些主题并对其他属于基本范畴的特殊方法进行深入介绍。
尽管是基础的范畴,仍可以针对其他比较深入的主题进行讨论。这里将会以基础的几个特殊方法作为开始,后续会讨论一些高级的特殊方法。
__init__()函数为对象的初始化操作提供了很大的自由度,对于不可变(每次操作都会产生一个新实例)的对象而言,声明和定义是非常重要的。在第1章中,我们会讨论一些关于这个函数设计的方案。
第1章 __init__()方法
__init__()方法的重要性体现在两点。首先,初始化既是对象生命周期的开始,也是非常重要的一个步骤,每个对象都必须正确地执行了初始化才能够正常地工作。其次,__init()__方法的参数可以多种形式来完成赋值。
因为__init()__方法传参方式的多样化,意味着对象的初始化过程也会有多种。关于这一点我们将使用一些有代表性的例子对此进行详细说明。
在深入讨论__init__()函数之前,需要看一下Python语言的类层次结构。简单地说,所有的类都可以继承object类,在自定义类中可以提供比较操作的默认实现。
本章会演示简单对象初始化的不同形式(例如,打牌)。随后将深入探讨复杂对象的初始化过程,涉及集合以及使用策略和状态模式实现的玩家类。
1.1 隐式的基类——object
每个Python类的定义都会隐式继承自object类,它的定义非常简单,几乎什么行为都不包括。我们可以创建一个object实例,但很多事情无法完成,因为很多特殊方法的调用程序都会抛出异常。
对于任何自定义类,都会隐式继承object。以下是一个类定义的示例(隐式继承了object类)。
class X:
pass
下面是对自定义类进行交互的代码。
>>> X.__class__
<class 'type'>
>>> X.__class__.__base__
<class 'object'>
可以看到类定义就是对type类的一个对象的类型声明,基类为object。
相应地,派生自object类中的对象方法也将继承各自相应的默认实现。在某些情况下,基类中一些特殊方法的默认行为也正是我们想要的。对于一些特殊情况,就需要重写这些方法。