Python 避免昂贵的“初始”是使用“新”的好理由吗?

Python 避免昂贵的“初始”是使用“新”的好理由吗?,python,class,Python,Class,在我的项目中,我们有一个基于集合的类。它可以从字符串、字符串的iterable(如元组)或其他自定义类初始化。当使用iterable初始化时,如果每个项不是一个自定义类,它会将其转换为一个特定的自定义类 因为它可以从各种数据结构中初始化,所以在这个类上操作的许多方法(如\uuuu和

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

因为它可以从各种数据结构中初始化,所以在这个类上操作的许多方法(如
\uuuu和
)在接受什么方面都是自由的,只是将它们的参数转换成这个类(即初始化一个新实例)。我们发现这相当缓慢,因为参数已经是类的一个实例,并且有很多成员(它遍历所有成员并检查它们是否是正确的类型)

我在想,为了避免这种情况,我们可以向类添加一个
\uuuu new\uuuu
方法,只要传入的参数已经是类的实例,就直接返回它。这是否合理地使用了
\uuuuuuuuuuuuuuuuuu

添加
\uuuuuuuuuuuuuuuuu
方法不会解决您的问题。从以下文件中:

如果
\uuuu new\uuuuu()
返回
cls
的实例,则新实例的
\uuuu init\uuu()
方法将像
\uuuuu init\uuuu(self[,…])
一样被调用, 其中,
self
是新实例,其余参数是 与传递给
\uuuu new\uuuu()
的相同

换句话说,返回相同的实例不会阻止python调用
\uuuu init\uuu
。 您可以很容易地验证这一点:

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
通过这种方式,用户可以在API的速度和灵活性之间进行选择



unutbu提出了一种更简单的方法:在执行操作时使用
isinstance
而不是duck键入。

修改
\uuuuu和
以检查
是否存在isinstance(obj,CustomClass)
?不调用
CustomClass(obj)
?@unutbu是的,这是第一个想法,但事实证明,在很多地方,CustomClass只是在没有检查的情况下初始化的。尽管如此,这可能是正确的答案,在大多数地方添加
isinstance
检查。太好了,非常感谢。我没有仔细阅读这些文件。为什么你说“如果它是可变的,你就是在寻找隐藏的bug”?这也是关于set的一个非常有趣的观点,我以前从未意识到这种细微的差别,但它是有意义的,并且是API设计的一个很好的点。@pfctdayelise我的意思是,如果你处理可变数据,用户会希望构造函数复制它。例如,
t=[1,2,3];s=列表(t);s、 附加(4)->也可以修改t。。。WTF。但是,如果数据是不可变的(如
tuple
s),这不是问题,因为用户不能以意外更改其他对象的方式对数据进行操作。
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}