为什么Fraction使用__new__而不是__init__?

时间:2014-09-17 23:24:13

标签: python class

我正在尝试创建一个新的不可变类型,类似于内置的Fraction但不是从它派生的。分数类is created like this

# We're immutable, so use __new__ not __init__
def __new__(cls, numerator=0, denominator=None):
    ...
    self = super(Fraction, cls).__new__(cls)
    self._numerator = ...
    self._denominator = ...
    return self

但我不知道这与

有什么不同
def __init__(self, numerator=0, denominator=None):
    ...
    self._numerator = ...
    self._denominator = ...

创建具有相同值的2个Fraction对象不会创建指向同一对象/内存位置的2个标签(实际上,在评论中指出它并不常见这样做的类型。)

尽管有源代码注释,但它们实际上并不是不可变的:

f1 = Fraction(5)

f2 = Fraction(5)

id(f1), id(f2)
Out[35]: (276745136, 276745616)

f1._numerator = 6

f1
Out[41]: Fraction(6, 1)

f2
Out[42]: Fraction(5, 1)

id(f1)
Out[59]: 276745136

那么这样做有什么意义呢?

文档说

  

__new__()主要用于允许不可变类型的子类(如int,str或tuple)自定义实例创建。也是   通常在自定义元类中重写以自定义类   创建

因此,如果我不是内置类型的子类,但我从头开始创建一个不可变类型(object的子类),我还需要使用它吗?

1 个答案:

答案 0 :(得分:4)

如果你要创建一个真正的不可变类型,你应该使用__new__,因为传递给__init__ self 对象在逻辑上已经是不可变的,所以它也是迟到将值分配给其成员。对于那些编写子类的人来说,这更为激烈,因为禁止添加成员。

由于不变性实际上不是内在属性,而是一种技巧,通常通过挂钩__setattr__强制执行,人们会编写用__init__初始化然后 make 自己的不可变类型通过设置某个成员然后使其他成员无法设置而不可变。但是在这种情况下的逻辑可能变得非常曲折,__setattr__可能会被额外的规则所包围。

使用某种可变类型更有意义,并使用__setattr__的版本继承其中的不可变类型,只需引发是子类中包含的异常。这使得使用__new__的逻辑变得明显。既然它可以创建可变超类并对其进行修改,但是然后将其作为继承类型返回,则不会产生混淆。

如果 Fraction 意图是不可变的,那么实施者要么错过了这一步,要么以后想得更好,忘了删除他们的评论。

>>> class Pair(object):
...     def __init__(self, key, value):
...         self.key = key
...         self.value = value
...
>>> class ImPair(Pair):
...     def __new__(cls, key, value):
...         self = Pair(key, value)
...         self.__class__ = cls
...     def __setattr__(self, name, value):
...         raise AttributeError(name)
...    
>>> x = Pair(2,3)
>>> x.key
2
>>> x.key = 9
>>> x.key
9
>>> x = ImPair(2,3)
>>> x.key
2
>>> x.key = 9
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __setattr__
AttributeError: key
>>>