抽象基类,要求实例具有最少的类属性,但允许额外的



我已经看到了一些与我的问题相关的问题,但我仍然无法提出一个适当的方法。

我有以下基类和子类:

class Base:
__attrs__ = {'one', 'two', 'three'}
def __init__(self, nums: {str} = None):
if nums:
self.__attrs__.update(nums)
self.nums = self.__attrs__

class Child(Base):
__attrs__ = {'four', 'five', 'six'}
def __init__(self, nums: {str} = None):
# self.__attrs__.update(super().__attrs__.copy())  <-----this works when uncommented
super(Child, self).__init__(nums)

b = Base()
print(b.nums)
c = Child()
print(c.nums)
# The output, as expected.
{'three', 'two', 'one'}
{'four', 'five', 'six'}

在实例化过程中,子类显然覆盖了基类的__attrs__值。我想弄清楚的是,我如何从基类继承__attrs__的值和扩展的子类-这样的输出将如下:

{'three', 'two', 'one','four', 'five', 'six'}

但不一定(或不太可能)按这个顺序。子类"__init__"的第一行将父类的属性复制到实例并实现最终结果。然而,由于我计划在这里有很多的集合,我正试图在基类中找到一种方法来实现这一点。

这是__init_subclass__的工作,它是一个可以在基类上定义的类方法,将被调用来初始化任何子类。

>>> class Base:
...     attrs = {'one', 'two', 'three'}
...     def __init_subclass__(cls): # cls will be a subclass!
...         cls.attrs.update(super(cls, cls).attrs)
...
>>> class Child(Base):
...     attrs = {'four', 'five'}
...
>>> Child.attrs
{'two', 'four', 'three', 'five', 'one'}
>>> Base.attrs
{'two', 'one', 'three'}

注意,在这种情况下,我们显式地将cls作为两个参数传递给super。否则,如果使用零参数形式,Foo将作为第一个参数传递,并且实际上需要子类!这是一种特殊情况,您几乎不希望在常规类方法(或实例方法!)中这样做。

注意,像往常一样,__init_subclass__是用来帮助您避免实现元类的。您可以像下面这样使用元类来做到这一点,尽管它有点笨拙,因为您不能假设父类有一个属性。

>>> class AttrsMeta(type):
...     def __init__(self, *args, **kwargs):
...         try:
...             parent_attrs = super(self, self).attrs
...         except AttributeError: # parent no attrs, base class?
...             return # handle however you like
...         self.attrs.update(parent_attrs) # assume the class always has an attrs defined
...
>>> class Base(metaclass=AttrsMeta):
...     attrs = {'one', 'two', 'three'}
...
>>> class Child(Base):
...     attrs = {'four', 'five'}
...
>>> Child.attrs
{'two', 'four', 'three', 'five', 'one'}
>>> Base.attrs
{'two', 'one', 'three'}

再次注意super(self, self)

如果所有这些对您的口味来说有点太神奇/太含蓄,我可能倾向于同意,您总是可以定义一个装饰器,我会使用类似这样的API:

>>> def attrs(cls):
...     def update_attrs(subclass):
...         subclass.attrs.update(super(subclass, subclass).attrs)
...         return subclass
...     cls.update_attrs = update_attrs
...     return cls
...
>>> @attrs
... class Base:
...     attrs = {'one', 'two', 'three'}
...
>>> @Base.update_attrs
... class Child(Base):
...     attrs = {'four', 'five'}
...
>>> Child.attrs
{'two', 'four', 'three', 'five', 'one'}
>>> Base.attrs
{'two', 'one', 'three'}

你可以改变你的基类来添加它自己的属性:

class Base:
__attrs__ = {'one', 'two', 'three'}
def __init__(self, nums: {str} = None):
self.__attrs__.update(Base.__attrs__)  # <-- add this.
if nums:
self.__attrs__.update(nums)
self.nums = self.__attrs__

相关内容

  • 没有找到相关文章

最新更新