python:抽象基类'__init __():初始化还是验证?

max*_*max 16 python oop abstract-class software-design

class ABC是一个"抽象基类".class X是它的子类.

有些工作需要在任何子类中完成ABC,这很容易忘记或做错.我想ABC.__init__()通过以下两种方式来帮助解决这些错误:

(1)开始这项工作,或(2)验证它

这会影响super().__init__()是在开始时还是在结束时调用X.__init__().

以下是用于说明目的的简化示例:

假设每个子类ABC必须具有一个属性registry,并且它必须是一个列表.ABC.__init__()可以(1)初始化registry或(2)检查它是否正确创建.以下是每种方法的示例代码.

方法1:在ABC中初始化

class ABC:
    def __init__(self):
        self.registry = []

class X:
    def __init__(self):
        super().__init__()
        # populate self.registry here
        ...
Run Code Online (Sandbox Code Playgroud)

方法2:在ABC中验证

class ABC:
    class InitializationFailure(Exception):
        pass
    def __init__(self):
        try:
            if not isinstance(self.registry, list):
                raise ABC.InitializationError()
        except AttributeError:
            raise ABC.InitializationError()

class X:
    def __init__(self):
        self.registry = []
        # populate self.registry here
        ...
        super().__init__()
Run Code Online (Sandbox Code Playgroud)

哪个更好的设计?

小智 14

当然,人们更倾向于方法1接近2(因为方法2将基础降级为标记接口而不是实现抽象功能).但是,方法1本身并不能满足您防止子类型开发人员忘记正确实现super()调用,确保初始化的目标.

您可能希望查看"工厂"模式以减轻子类型实现者忘记初始化的可能性.考虑:

class AbstractClass(object):
    '''Abstract base class template, implementing factory pattern through 
       use of the __new__() initializer. Factory method supports trivial, 
       argumented, & keyword argument constructors of arbitrary length.'''

   __slots__ = ["baseProperty"]
   '''Slots define [template] abstract class attributes. No instance
       __dict__ will be present unless subclasses create it through 
       implicit attribute definition in __init__() '''

   def __new__(cls, *args, **kwargs):
       '''Factory method for base/subtype creation. Simply creates an
       (new-style class) object instance and sets a base property. '''
       instance = object.__new__(cls)

       instance.baseProperty = "Thingee"
       return instance
Run Code Online (Sandbox Code Playgroud)

这个基类可以比方法1更简单地扩展,只使用三(3)行代码san-commment,如下所示:

class Sub(AbstractClass):
   '''Subtype template implements AbstractClass base type and adds
      its own 'foo' attribute. Note (though poor style, that __slots__
      and __dict__ style attributes may be mixed.'''

   def __init__(self):
       '''Subtype initializer. Sets 'foo' attribute. '''
       self.foo = "bar"
Run Code Online (Sandbox Code Playgroud)

请注意,虽然我们没有调用超类的构造函数,但是baseProperty将被初始化:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from TestFactory import *
>>> s = Sub()
>>> s.foo
'bar'
>>> s.baseProperty
'Thingee'
>>> 
Run Code Online (Sandbox Code Playgroud)

正如其注释所示,基类AbstractClass不需要使用,它可以通过在new()初始化器中设置它们来轻松地"隐式"定义属性.例如:

instance.otherBaseProperty = "Thingee2"
Run Code Online (Sandbox Code Playgroud)

会工作得很好.另请注意,基类的初始化程序在其子类型中支持普通(无arg)初始化程序,以及可变长度的arugmented和关键字参数初始值设定项.我建议始终使用此表单,因为它不会在最简单(简单的构造函数)情况下强加语法,但允许更复杂的功能而不强加维护.