避免昂贵的__init__是使用__new__的一个很好的理由吗?

时间:2014-08-28 08:05:55

标签: python class

在我的项目中,我们有一个基于set的类。它可以从字符串或字符串的可迭代(例如元组)或其他自定义类初始化。当使用iterable初始化时,它会将每个项目转换为特定的自定义类(如果它不是已经存在的类别)。

因为它可以从各种数据结构初始化,许多在这个类上运行的方法(例如__and__)在它们接受的内容中是自由的,只是将它们的参数转换为这个类(即初始化)一个新的实例)。当参数已经是类的一个实例,并且有很多成员时(我们正在迭代它们并检查它们是正确的类型),我们发现这是相当慢的。

我想要避免这种情况,我们可以在类中添加__new__方法,并且只要传入的参数已经是类的实例,就直接返回它。这是合理使用__new__吗?

1 个答案:

答案 0 :(得分:3)

添加 __new__方法无法解决您的问题。来自__new__的文档:

  

如果__new__()返回cls的实例,则返回新实例   将调用__init__()方法,如__init__(self[, ...]),   其中self是新实例,其余参数是   与传递给__new__()的内容相同。

换句话说,返回相同的实例将阻止python调用__init__。 您可以非常轻松地验证这一点:

In [20]: class A:
    ...:     def __new__(cls, arg):
    ...:         if isinstance(arg, cls):
    ...:             print('here')
    ...:             return arg
    ...:         return super().__new__(cls)
    ...:     def __init__(self, values):
    ...:         self.values = list(values)

In [21]: a = A([1,2,3])

In [22]: A(a)
here
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-22-c206e38274e0> in <module>()
----> 1 A(a)

<ipython-input-20-5a7322f37287> in __init__(self, values)
      6         return super().__new__(cls)
      7     def __init__(self, values):
----> 8         self.values = list(values)

TypeError: 'A' object is not iterable

如果实现__init__,只有__new__ 可以能够完成这项工作。我相信这是tuple所做的。

只有当你的类是不可变的时(例如tuple这样做),这种行为才是可接受的,因为结果是明智的。如果它是可变的,你就会要求隐藏的错误。

更明智的方法是执行set所做的事情:__*__操作仅在set上运行 ,但set也提供了命名适用于任何可迭代的方法:

In [30]: set([1,2,3]) & [1,2]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-30-dfd866b6c99b> in <module>()
----> 1 set([1,2,3]) & [1,2]

TypeError: unsupported operand type(s) for &: 'set' and 'list'

In [31]: set([1,2,3]) & set([1,2])
Out[31]: {1, 2}

In [32]: set([1,2,3]).intersection([1,2])
Out[32]: {1, 2}

通过这种方式,用户可以在API的速度和灵活性之间进行选择。


更简单的方法是unutbu提出的方法:在实现操作时使用isinstance而不是duck-typing。