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

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

2.8 __new__()方法和不可变对象

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

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

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

class Float_Fail( float ):
   def __init__( self, value, unit ):
     super().__init__( value )
     self.unit = unit

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

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

>>> s2 = Float_Fail( 6.5, "knots" )
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: float() takes at most 1 argument (2 given)

可以看到,对于内置的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类的正确方式。

class Float_Units( float ):
   def __new__( cls, value, unit ):
     obj= super().__new__( cls, value )
     obj.unit= unit
     return obj

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

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

>>>speed= Float_Units( 6.5, "knots" )
>>>speed
6.5
>>>speed * 10
65.0
>>> speed.unit
'knots'

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

上一篇:Python编程:从python中理解面向对象


下一篇:为什么云安全离不开“可视性即服务”