Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python:抽象基类'__初始化():初始化还是验证?_Python_Oop_Abstract Class_Software Design - Fatal编程技术网

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

python:抽象基类'__初始化():初始化还是验证?,python,oop,abstract-class,software-design,Python,Oop,Abstract Class,Software Design,类ABC是一个“抽象基类”类X是它的子类 在ABC的任何子类中都有一些工作需要完成,这些工作很容易忘记或做得不正确。我想通过以下两种方法来帮助发现此类错误: (1) 开始那项工作,或者 (2) 验证它 这会影响是在X.\uuuu init\uuuuuuuuuuuuuuuuuuuuuuu()的开始还是结束时调用super() 以下是一个简化示例,用于说明: 假设ABC的每个子类都必须有一个属性registry,并且它必须是一个列表ABC.\uuuu init\uuuuu()可以(1)初始化注册表或

类ABC
是一个“抽象基类”<代码>类X是它的子类

ABC
的任何子类中都有一些工作需要完成,这些工作很容易忘记或做得不正确。我想通过以下两种方法来帮助发现此类错误:

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

这会影响是在
X.\uuuu init\uuuuuuuuuuuuuuuuuuuuuuu()
的开始还是结束时调用
super()

以下是一个简化示例,用于说明:

假设
ABC
的每个子类都必须有一个属性
registry
,并且它必须是一个列表<代码>ABC.\uuuu init\uuuuu()可以(1)初始化
注册表或(2)检查它是否正确创建。下面是每种方法的示例代码

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

哪个设计更好?

第一个设计更好,因为子类不需要知道您已经用列表实现了注册表。例如,您可以提供一个
\u is\u in\u registry
函数,该函数接受一个参数,并返回元素是否在注册表中。然后,您可以稍后更改超类并用集合替换列表,因为元素只能在注册表中出现一次,并且您不需要更改子类


此外,代码更少:假设您在
ABC
中有100个这样的字段,
ABC
有100个子类,如
X

在您提供的示例中,我将按照您的方法1进行操作。但是,我认为ABC类主要是X和其他实现特定接口的类的实现助手。所述接口由属性“注册表”组成

至少在逻辑上,您应该区分X和其他类共享的接口,以及帮助您实现它的基类。也就是说,单独定义有一个接口(例如“ABC”),它公开一个列表“registry”。然后,您可以决定将接口的实现作为接口ABC的实现者的公共基类(概念上是一种混合)进行分解,因为这样可以很容易地引入新的实现类(除了X)


编辑:关于在实现类时防止错误,我将通过单元测试来实现这一点。我认为这比试图解释实现中的所有内容更全面:)

当然,人们更喜欢方法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
与方法1相比,该基类的扩展更为简单,只需使用三(3)行代码即可实现,如下所示:

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"
请注意,尽管我们没有调用超级类的构造函数,但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不需要使用插槽,它可以通过在其新的()初始值设定项中设置属性来轻松地“隐式”定义属性。例如:

instance.otherBaseProperty = "Thingee2"

那就行了。还要注意,基类的初始值设定项在其子类型中支持普通(无arg)初始值设定项,以及可变长度的分段和关键字参数初始值设定项。我建议始终使用此表单,因为它不会在最简单(琐碎的构造函数)的情况下强加语法,而是允许在不强加维护的情况下实现更复杂的功能

听起来很合理。所以当我在子类中填充它时,我应该尽量不依赖于
注册表
作为一个列表,对吗?也就是说,我应该尝试使用
self.add_to_registry(new_item)
,而不是免费使用
registry.append(new_item)
,它将在ABC中定义为
.append()
,但如果需要,可以在以后更改为
。add()
。@max我就是这样做的,是的。我不确定这是否是“pythonic”的方式,我只是从python开始,我更多的是来自静态语言背景。但是如果你不添加这些函数,为什么还要构建一个抽象的超类呢?好吧,我可能会用ABC来验证一些东西。。但我同意,似乎做一些实际工作更有用。