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

发布于 2024-10-19 13:53:26 字数 1431 浏览 4 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

热鲨 2024-10-26 13:53:26

当然,与方法 2 相比,人们更喜欢方法 1(因为方法 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 不需要使用 ,它可以通过在其 new() 初始值设定项中设置属性来轻松“隐式”定义属性。例如:

instance.otherBaseProperty = "Thingee2"

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

Certainly, one prefers approach 1 to approach 2 (as approach 2 relegates the base to a tag interface rather than fulfilling abstract functionality). But, approach 1 doesn't, by itself, meet your goal of preventing the subtype developer from forgetting to implement the super() call properly, ensuring initialization.

you may want to look into the "Factory" pattern to alleviate the possibility of subtype implementers forgetting initialization. Consider:

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

This base class can be extended more trivially than in approach 1, using only three (3) lines of code san-commment, as follows:

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"

Note that though we didn't call the super-class' constructor, the baseProperty will be initialized:

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'
>>> 

As its comment indicates, the base class AbstractClass need not use slots, it could just as easily 'implicitly' define attributes by setting them in its new() initializer. For instance:

instance.otherBaseProperty = "Thingee2"

would work fine. Also note that the base class' initializer supports trivial (no-arg) initializers in its subtypes, as well as variable-length arugmented and keyword argument initializers. I recommend always using this form as it doesn't impose syntax in the simplest (trivial constructor) case but allows for the more complex functionality without imposing maintenance.

栖迟 2024-10-26 13:53:26

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

至少在逻辑上,您应该区分 X 和其他类共享的接口以及帮助您实现它的基类。即,单独定义有一个接口(例如“ABC”),它公开一个列表“registry”。然后,您可能决定将接口的实现分解为接口 ABC 的实现者的公共基类(概念上的混合),因为它使得引入新的实现类(除了 X 之外)变得非常容易。

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

In the example you have provided, I would do it as in your approach 1. I would see class ABC mainly as an implementation helper for X and other classes that implement a certain interface, however. Said interface consists of the attribute 'registry'.

You should, logically at least, discern between the interface shared by X and other classes, and the baseclass which helps you implement it. That is, define separately that there is an interface (for example "ABC"), which exposes a list "registry". Then, you may decide to factor out the implementation of the interface as a common baseclass (conceptually a mix-in) to implementors of interface ABC, since it makes it very easy to introduce new implementing classes (in addition to X).

Edit: With regard to guarding against mistakes in implementing classes, I'd target this through unit tests. I think this is more comprehensive than trying to account for everything in your implementation :)

Saygoodbye 2024-10-26 13:53:26

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

而且,代码也更少:想象一下,ABC 有 100 个这样的字段,而 ABC 有 100 个像 X 这样的子类...

The first is the better design, because the subclasses don't need to know you have implemented the registry with a list. For instance, you could offer an _is_in_registry function which takes one argument, and returns whether the element is in the registry. You could then later change the superclass and replace the list by a set, because elements canonly appear once in the registry, and you wouldn't need to change the subclasses.

Also, it is less code: Imagine you have 100 fields like this in ABC and ABC has 100 subclasses like X...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文