【问题标题】:How can I use abstract class properties with metaclasses?如何将抽象类属性与元类一起使用?
【发布时间】:2021-08-21 01:30:33
【问题描述】:

我使用元类为 Parent 类创建了一个抽象类属性:

from abc import abstractmethod, ABCMeta

class ParentMeta(ABCMeta):
    @property
    @abstractmethod
    def CONSTANT(cls):
        raise NotImplementedError()

class Parent(metaclass=ParentMeta):
    pass

我可以为它设置一个值,如下所示:

class ChildMeta(ParentMeta):
    CONSTANT = 4


class Child(Parent, metaclass=ChildMeta):
    pass


print(Child.CONSTANT) // 4

是否也可以在不经过额外元类的情况下为其赋值?比如如下?

class OtherChild(Parent):
    CONSTANT = 5

OtherChild.CONSTANT // NotImplementedError

【问题讨论】:

    标签: python properties abstract metaclass


    【解决方案1】:

    带有抽象方法修饰符的CONSTANT 的声明应该在基类(父类)上,而不是在元类上。 您根本不必为此干预元类,只需使用 abc.ABC 作为您的基类:

    In [14]: import abc                                                                                                                                           
    
    In [15]: class Parent(abc.ABC): 
        ...:     @property 
        ...:     @abc.abstractmethod 
        ...:     def CONSTANT(self): pass 
        ...:                                                                                                                                                      
    
    In [16]: class Child1(Parent): 
        ...:     CONSTANT = 5 
        ...:                                                                                                                                                      
    
    In [17]: Child1()                                                                                                                                             
    Out[17]: <__main__.Child1 at 0x7fc55246b670>
    
    In [18]: class Child2(Parent): 
        ...:     pass 
        ...:      
        ...:                                                                                                                                                      
    
    In [19]: Child2()                                                                                                                                             
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-19-59958dc9047d> in <module>
    ----> 1 Child2()
    
    TypeError: Can't instantiate abstract class Child2 with abstract method CONSTANT
    

    至于在元类本身中声明 ABC 模块的东西的“抽象属性”:这不是我们的本意,如果你有任何接近你的意图的东西,那真是太幸运了。

    这个想法是 abc.ABCMeta + 语言核心中的一些机制提供了在类本身而不是元​​类中检查抽象属性和方法的机制。

    类中定义的属性已经是类属性。

    由于 Python 中对象模型的极端一致性,如果在元类上创建 property 以完全不相关的方式(与抽象类无关)将作为“类属性”工作:在这种情况下,类表现为元类,它们使用元类上的属性。但是,在元类上设置属性和属性以在类上反映和查看在正常设计中是极其罕见的。阅读您的问题,您似乎需要一个正常的类属性,如上。

    如果你想让类级别的东西表现得像一个实际的属性(当访问属性时运行代码,所以它是动态生成的),可以通过创建一个类似于属性的描述符类来实现也适用于课程 - 或者,只需在元类上使用property,因为你已经完成了一半。如果您只是想检查是否在每个子类中声明了该属性,同样,abc 的简单用法就是您所需要的。

    否则,如果您依赖于真实属性(不仅仅是一种声明“抽象属性”的方式),并且使用元类属性机制,那么您当然必须创建一个中间元类来覆盖它:类上的property 适用于实例,而不适用于类本身。

    有一些机制可以通过在元类__new__方法上实际使用一些代码来使用——例如,可以有一个标记装饰器,它可以使在类上声明的属性被“移植”到创建类的元类,并作为类属性发挥作用,只是让简单的使用 abc.ABC 来处理抽象方法部分。由于它似乎不是您所需要的,所以我不会完全实现它:正确完成它需要一些时间。

    【讨论】:

      猜你喜欢
      • 2011-03-07
      • 2020-10-06
      • 1970-01-01
      • 2020-12-30
      • 1970-01-01
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多