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

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

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

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

    class FloatFail( 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 FloatUnits( 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章“创建数值类型”中介绍。