__init__()方法意义重大的原因有两个。第一个原因是在对象生命周期中初始化是最重要的一步;每个对象必须正确初始化后才能正常工作。第二个原因是__init__()参数值可以有多种形式。
因为有很多种方式为__init__()提供参数值,对于对象创建有大量的用例,我们可以看看其中的几个。我们想尽可能的弄清楚,因此我们需要定义一个初始化来正确的描述问题区域。
在我们接触__init__()方法之前,无论如何,我们都需要粗略、简单地看看在Python中隐含的object类的层次结构。
在这一章,我们看看不同形式的简单对象的初始化(例如:打牌)。在这之后,我们还可以看看更复杂的对象,就像包含集合的hands对象以及包含策略和状态的players。
隐含的超类——object
每一个Python类都隐含了一个超类:object。它是一个非常简单的类定义,几乎不做任何事情。我们可以创建object的实例,但是我们不能用它做太多,因为许多特殊的方法容易抛出异常。
当我们自定义一个类,object则为超类。下面是一个类定义示例,它使用新的名称简单的继承了object:
class X: pass
下面是和自定义类的一些交互:
>>> X.__class__<class 'type'>>>> X.__class__.__base__<class 'object'>
我们可以看到该类是type类的一个对象,且它的基类为object。
就像在每个方法中看到的那样,我们也看看从object继承的默认行为。在某些情况下,超类特殊方法的行为是我们所想要的。在其他情况下,我们需要覆盖这个特殊方法。
基类对象的init()方法
对象生命周期的基础是创建、初始化和销毁。我们将创建和销毁的高级特殊方法推迟到后面的章节中,目前只关注初始化。
所有类的超类object,有一个默认包含pass的__init__()实现,我们不需要去实现__init__()。如果不实现它,则在对象创建后就不会创建实例变量。在某些情况下,这种默认行为是可以接受的。
我们总是给对象添加属性,该对象为基类object的子类。思考以下类,需要两个实例变量但不初始化它们:
class Rectangle: def area(self): return self.length * self.width
Rectangle类有一个使用两个属性来返回一个值的方法。这些属性没有初始化。这是合法的Python代码。它可以有效的避免专门设置属性,虽然感觉有点奇怪,但是有效。
下面是于Rectangle类的交互:
>>> r = Rectangle()>>> r.length, r.width = 13, 8>>> r.area()104
显然这是合法的,但也是容易混淆的根源,所以也是我们需要避免的原因。
无论如何,这个设计给予了很大的灵活性,这样有时候我们不用在__init__()方法中设置所有属性。至此我们走的很顺利。一个可选属性其实就是一个子类,只是没有真正的正式声明为子类。我们创建多态在某种程度上可能会引起混乱以及if语句的不恰当使用所造成的盘绕。虽然未初始化的属性可能是有用的,但很有可能是糟糕设计的前兆。
新闻热点
疑难解答