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

标签 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) 检查它是否已正确创建。以下是每种方法的示例代码。

方法一:在ABC中初始化

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

class X:
    def __init__(self):
        super().__init__()
        # populate self.registry here
        ...

方法 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__()

哪个设计更好?

最佳答案

当然,与方法 2 相比,人们更喜欢方法 1(因为方法 2 将基础委托(delegate)给标记接口(interface),而不是实现抽象功能)。但是,方法 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

这个基类可以比方法 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"

请注意,虽然我们没有调用父类(super class)的构造函数,但 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'
>>> 

正如它的评论所指出的,基类 AbstractClass 不需要使用slots,它可以通过在其 new() 初始化器中设置它们来轻松地“隐式”定义属性.例如:

instance.otherBaseProperty = "Thingee2"

会很好用。另请注意,基类的初始值设定项在其子类型中支持普通(无参数)初始值设定项,以及可变长度的参数初始值设定项和关键字参数初始值设定项。我建议始终使用这种形式,因为它不会在最简单的(普通构造函数)情况下强加语法,而是允许在不强加维护的情况下实现更复杂的功能。

关于python:抽象基类'__init__():初始化还是验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5133262/

相关文章:

抽象类中的php get_object_vars

Java 未经检查的重写返回类型

python - 为什么使用合并操作生成的数据帧不是 3x3 维度而不是 3x5 维度?

java - 找到两个线性等式成立的整数集

javascript - 无法读取数组的属性

c++ - 覆盖返回基类型的函数

python - 使用间隔屏蔽 numpy 数组

python - paramiko sftp 无法删除远程文件夹,ioerror

javascript - Nodejs 为什么用原型(prototype)静态初始化数组?

c++ - 类定义中静态常量成员map<>的初始化