【问题标题】:How to enforce a child class to set attributes using abstractproperty decorator in python?如何强制子类在 python 中使用 abstractproperty 装饰器设置属性?
【发布时间】:2017-01-17 11:24:18
【问题描述】:

我想强制一个子类设置几个属性(实例变量),以实现我在 python 2.7 中使用抽象属性装饰器。下面是示例代码:

from abc import ABCMeta, abstractproperty
class Parent(object):
    __metaclass__ = ABCMeta
    @abstractproperty
    def name(self):
        pass

class Child1(Parent):
    pass

class Child2(Parent):
    name = 'test'

class Child3(Parent):
    def __init__(self):
        self.name = 'test'

obj_child1 = Child1()

Child1 对象创建按预期给出错误。

obj_child2 = Child2()

Child2 对象创建工作正常,因为设置了抽象属性“名称”

然而

 obj_child3 = Child3()

给出TypeError: Can't instantiate abstract class Child3 with abstract methods name

我不明白:虽然属性'name'是在init中设置的 方法,为什么创建 Child3 对象会抛出类型错误。

我的要求是在子类的方法中设置属性。解释 child2 类有什么问题以及是否有更好的方法来强制子类在方法中设置属性将对我有所帮助。提前致谢。

【问题讨论】:

  • 你的标题是什么意思?你的意思是说,“我不明白……”
  • 我认为问题标题现在更有意义@Tagc

标签: python python-2.7 abstract-class


【解决方案1】:

这就是我认为正在发生的事情。

当您尝试实例化Child1 时,您还没有提供抽象属性name 的实现。你可能已经这样做了:

class Child1(Parent):
    @property
    def name(self):
        return "foo"

由于该类不提供从其父类继承的所有抽象方法的实现,因此它本身实际上是一个抽象类,因此无法实例化。尝试实例化它会给你你的TypeError

对于Child2,您将其定义为:

class Child2(Parent):
    name = 'test'

在这种情况下,您将覆盖类的name 属性,使其不再引用需要实现的抽象属性。这意味着Child2 不是抽象的,可以被实例化。

我注意到的一个区别是,当您实现 name = 'test' 时,vars(Child2) 返回如下输出:

{..., '__abstractmethods__': frozenset(), ..., 'name': 'test'}

但是,当您将其更改为 foo = 'test' 之类的内容时,您会得到:

{..., '__abstractmethods__': frozenset({'name'}), ..., 'foo': 'test'}

请注意,在您定义name = 'test' 的情况下,即Child2 可以实例化时,__abstractmethods__ 属性是一个空集。

最后,对于Child3,您必须记住,抽象属性存储为类本身的属性,您无需在任何地方重新定义。

一旦您尝试创建它的实例,解释器就会发现它缺少至少一个抽象方法的实现,并抛出您看到的TypeError。它甚至没有到达构造函数中的赋值self.name = 'test'


回答您关于如何强制 子项在他们可以像您一样执行某些操作时始终为抽象属性/方法提供实现的第二部分 - 我实际上不确定这是否可能。 Python 是成年人同意的语言。

【讨论】:

  • 感谢您的观察,我不明白为什么 Child3 对象创建会出错。 Child1 和 Child2 对象的创建对我来说很清楚。
  • @daved 按照您的评论进行了编辑。 :)
  • 感谢您的回答,它帮助我了解@Tagc 发生了什么
【解决方案2】:

一个更简单的解决方案将是

class Parent(object):
    name = None
    def __init__(self):
        if self.name == None:
            raise NotImplementedError('Subclasses must define bar')

class Child1(Parent):
    pass

class Child2(Parent):
    name = 'test'

class Child3(Parent):
    def __init__(self)
        self.name = 'test'

obj1 = Child1() 引发 NotImplementedError

obj2 = Child2() 工作正常

obj3 = Child3() 工作正常。这是您需要强制子类设置属性name 并从方法设置属性。

【讨论】:

    【解决方案3】:

    你可以这样做, 父类:

    class Parent(object):
        __metaclass__ = ABCMeta
        @abstractproperty
       def name(self):
           pass
    

    儿童班:

    class Child(Parent):
        name = None
        def __init__(self):
            self.name = 'test'
    

    现在

    obj = Child() obj.name

    给出“测试”所需的输出。

    通过这样做,您可以强制一个类设置父类的属性,而在子类中您可以从方法中设置它们。 这不是您需要的完美解决方案,但它很接近。此解决方案的唯一问题是您需要在子类中将父类的所有抽象属性定义为None,然后使用方法对其进行设置。

    【讨论】:

      猜你喜欢
      • 2021-03-30
      • 1970-01-01
      • 2014-07-09
      • 1970-01-01
      • 1970-01-01
      • 2011-11-20
      • 2021-12-15
      • 1970-01-01
      • 2020-10-07
      相关资源
      最近更新 更多