简体   繁体   English

避免使用昂贵的__init__是使用__new__的充分理由吗?

[英]Is avoiding expensive __init__ a good reason to use __new__?

In my project, we have a class based on set. 在我的项目中,我们有一个基于集合的类。 It can be initialised from a string, or an iterable (eg tuple) of strings, or other custom classes. 它可以从字符串或字符串的可迭代(例如元组)或其他自定义类初始化。 When initialised with an iterable it converts each item to a particular custom class if it is not one already. 当使用可迭代方法初始化时,如果尚未将每个项目转换为特定的自定义类,则将其转换为一个特定的自定义类。

Because it can be initialised from a variety of data structures a lot of the methods that operate on this class (such as __and__ ) are liberal in what they accept and just convert their arguments to this class (ie initialise a new instance). 因为可以从各种数据结构中初始化它,所以在该类上运行的许多方法(例如__and__ )在接受内容上都是自由的,只需将其参数转换为此类(即初始化一个新实例)即可。 We are finding this is rather slow, when the argument is already an instance of the class, and has a lot of members (it is iterating through them all and checking that they are the right type). 当参数已经是该类的实例并且有很多成员时,我们发现它相当慢(正在遍历所有成员并检查它们是否是正确的类型)。

I was thinking that to avoid this, we could add a __new__ method to the class and just if the argument passed in is already an instance of the class, return it directly. 我当时想避免这种情况,我们可以在类中添加__new__方法,并且即使传入的参数已经是该类的实例,也可以直接将其返回。 Would this be a reasonable use of __new__ ? 这是对__new__的合理使用吗?

Adding a __new__ method will not solve your problem. 添加 __new__方法不会解决您的问题。 From the documentation for __new__ : __new__的文档中:

If __new__() returns an instance of cls , then the new instance's __init__() method will be invoked like __init__(self[, ...]) , where self is the new instance and the remaining arguments are the same as were passed to __new__() . 如果__new__()返回cls的实例,则将调用新实例的__init__()方法,就像__init__(self[, ...]) ,其中self是新实例,其余参数与传递给__new__()

In otherwords, returning the same instance will not prevent python from calling __init__ . 换句话说,返回相同的实例不会阻止python调用__init__ You can verify this quite easily: 您可以很容易地验证这一点:

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

You may be able to make this work if you did not implement __init__ at all, but only __new__ . 可以来完成这项工作,如果你没有实现__init__可言,但只有__new__ I believe this is what tuple does. 我相信这就是tuple所做的。

Also that behaviour would be acceptable only if your class is immutable (eg tuple does this), because the result would be sensible. 同样,只有当您的类是不可变的时,这种行为才是可以接受的(例如, tuple这样做),因为结果是明智的。 If it is mutable you are asking for hidden bugs. 如果它是可变的,则要求隐藏的错误。

A more sensible approach is to do what set does: __*__ operations operate only on set s, however set also provides named methods that work with any iterable: 一种更明智的方法是执行set操作: __*__操作仅对 set s进行操作,但是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}

In this way the user can choose between speed and flexibility of the API. 这样,用户可以在API的速度和灵活性之间进行选择。


A simpler approach is the one proposed by unutbu: use isinstance instead of duck-typing when implementing the operations. unutbu提出了一种更简单的方法:在执行操作时,使用isinstance而不是duck-typing。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM