0

I've created an abstract class property for class Parent using metaclasses:

from abc import abstractmethod, ABCMeta

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

class Parent(metaclass=ParentMeta):
    pass

I can set a value for it as follows:

class ChildMeta(ParentMeta):
    CONSTANT = 4


class Child(Parent, metaclass=ChildMeta):
    pass


print(Child.CONSTANT) // 4

Is it also possible to give it a value without going through an extra metaclass? For example, as follows?

class OtherChild(Parent):
    CONSTANT = 5

OtherChild.CONSTANT // NotImplementedError
vandenheuvel
  • 349
  • 2
  • 13

1 Answers1

0

The declaration of CONSTANT with the abstract method modifier should be on the base class (Parent), not on the metaclass. You don't have to meddle with metaclasses for this at all, just use abc.ABC as your base class:

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

As for "abstractproperties" declaring things of the ABC module in the metaclass themselves: that is not the intended us, and if you got anything close to your intent, that was sheer luck.

The idea is that abc.ABCMeta + some mechanisms in the language core provide the mechanism for abstract attributes and methods to be checked in the classes themselves, not in the metaclasses.

An attribute defined in a class is already a class attribute.

On a completly unrelated way (unrelated to abstract classes) property will work as a "class property" if created on the metaclass due to the extreme consistency of the object model in Python: classes in this case behave as instances of the metaclass, and them the property on the metaclass is used. However, setting properties and attributes on a metaclass to be reflected and viewed on the class is something extremely rare in a normal design. Reading your question, it just looks like you need a normal class attribute as above.

If you want something at class level to behave like an actual property (with code to be run when the attribute is accessed, so it is dynamically generated), it is possible by creating a descriptor class, akin to property, that would also work for classes - or, just use property on the metaclass as you have half done. If you just want to check if the attribute is declared in each child class, again, the plain use of abc is what you need.

Otherwise, if you are relying on real properties (not just a way to declare "abstractattribute"), and using the property-on-metaclass mechanism, of course you have to create an intermediary metaclass in order to override it: a property on the class would work for instances, not for the class itself.

There are mechanisms that could be used by actually having some code on the metaclass __new__ method- for example, it would be possible to have a marker decorator that could make a property declared on the class to be "transplanted" to the metaclass on class creation, and function as a class property, and just let the plain use of abc.ABC to handle the abstractmethod part. As it does not seem to be what you need, I won't implement it in full: it'd take sometime to be done correctly.

jsbueno
  • 99,910
  • 10
  • 151
  • 209