天天看点

《Python面向对象编程指南》——2.8 __new__()方法和不可变对象

本节书摘来自异步社区《python面向对象编程指南》一书中的第2章,第2.8节,作者[美]steven f. lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

<code>__new__</code>方法的一个用途是初始化不可变对象。__new__()方法中允许创建未初始化的对象。这允许我们在__init__()方法被调用之前先设置对象的属性。

由于不可变类的__init__()方法很难重载,因此__new__方法提供了一种扩展这种类的方法。

下面是一个错误定义的类,我们定义了float的一个包含单位信息的版本。

我们试图(不合理地)初始化一个不可变对象。

下面是当我们试图使用这个类时会发生的情况。

可以看到,对于内置的float类,我们不能简单地重载__init__方法。对于其他的内置不可变类型,也有类似的问题。我们不能在不可变对象self上设置新的属性值,因为这是不可变性的定义。我们只能在对象创建的过程中设置属性值,对象创建之后__new__()方法就会被调用。

__new__()方法天生就是一个静态方法。即使没有使用@staticmethod修饰符,它也是静态的。它没有使用self变量,因为它的工作是创建最终会被赋值给self变量的对象。

这种情况下,我们会使用的方法签名是__new__( cls, args, *kw)。cls变量是准备创建的类的实例。下一个部分关于元类型的例子,会比这里展示的args的参数序列更加复杂。

__new__()方法的默认实现如下。

return super().__new__( cls )将调用基类的__new__()方法创建对象。这个工作最终委托给了object.__new__(),这个方法创建了一个简单的空对象。除了cls以外,其他的参数和关键字最终都会传递给__init__()方法,这是python定义的标准行为。

除了有下面的两个例外,这就是我们期望的行为。

当我们需要继承一个不可变的类的时候,我们会在后面的部分详细讲解。

当我们需要创建一个元类型的时候,这是下一个部分的主题,因为它与创建不可变对象是完全不同的。

当创建一个内置的不可变类型的子类时,不能重载__init__()方法。取而代之的是,我们必须通过重载__new__()方法在对象创建的过程中扩展基类的行为。下例是扩展float类的正确方式。

上面的代码在对象创建的过程中设置了一个属性的值。

下面的代码使用上面定义的类创建了一个带单位的浮点数。

注意,像speed * 10这种表达式不会创建一个float_units对象。这个类的定义继承了float中所有的运算符;float的所有算术特殊方法也都只会创建float对象。创建float_units对象会在第7章“创建数值类型”中介绍。