如何使用.__ new__阻止超类的生成,同时允许其子类的生成?

时间:2019-05-15 05:05:03

标签: python

我有一个类“ A”,它具有子类“ B”,“ C”和D。 “ A”仅用于分类和继承,因此我不希望用户创建“ A”的实例。 但是,我想照常创建“ B”,“ C”和“ D”的实例。

首先,我通过覆盖A.__new__来阻止'A'实例的生成。 然后,我再次覆盖B.__new__,如下所示。

class A(object):
    def __new__(cls, *args, **kwargs):
        raise AssertionError('A cannot be generated directly.')

class B(A):
    def __new__(cls, *args, **kwargs):
        return super(cls,B).__new__(cls, *args, **kwargs)

但是,使用此代码,生成B会返回相同的AssertionError。 我了解到,由于A.__new__被禁用,super(cls,B).__new__(与A.__new__完全相同)也被禁用。 有什么方法可以生成子类的实例,而无需调用其超类的__new__

已编辑-已解决

我没有调用A.__new__,而是调用了更高超类(即“对象”)的__new__方法。此解决方案避免了这个问题。

新代码如下:

class A(object):
    def __new__(cls, *args, **kwargs):
        raise AssertionError('A cannot be generated directly.')

class B(A):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

请注意,它是return object.__new__(cls),而不是return object.__new__(cls, *args, **kwargs)。这是因为object.__new__除了cls之外没有其他参数,因此不必传递任何参数。

1 个答案:

答案 0 :(得分:1)

您正在A.__new__方法中显式调用B.__new__。这个版本运作良好:

class A:
    def __new__(cls, *args, **kwargs):
        raise AssertionError()

class B(A):
    def __new__(cls, *args, **kwargs):
       pass

结果:

>>> x = A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __new__
AssertionError
>>> y = B()
>>> y
>>>

请注意,B.__new__现在几乎没有用:我实际上没有得到B实例(y为空)。我假设您有实际的理由要修改__new__,在这种情况下,请将替换代码放在我有pass的位置。

如果您不要有充分的理由修改__new__,请不要。您几乎总是想要__init__来代替。

>>> class A1:
...     def __init__(self):
...             raise ValueError()
... 
>>> class B1(A1):
...     def __init__(self):
...             pass
... 
>>> A1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
ValueError
>>> B1()
<__main__.B1 object at 0x7fa97b3f26d8>

请注意,现在如何获取实际的默认构造的B1对象。

作为一般规则,如果要使用不可变类型(例如__new__str)或构建新的元类,则只需要修改tuple。您可以查看有关每种方法负责in the docs的详细信息。