pythonmetaclassabc

Checking subclass against metaclass type


I have a set of plugins that inherit from a metaclass. the metaclasss is defined like so:

from abc import ABC, abstractmethod

class MetaReader(ABC):
    def __init__(self, arg1, arg2, **kwargs):
        ...
        

and the subclass like so:

from utils.MetaReader import MetaReader
    class ABF2Reader(MetaReader):
        ...

This works, as far as I can tell, and I can instantiate classes and use them as intended. However, at some points I need to load these classes as plugins without instantiating them, and when I do, I need to check their subclass against the proper metaclass to know what to do with them when loaded. And there, I get some weird results:

from utils.MetaReader import MetaReader
from plugins.datareaders.ABF2Reader import ABF2Reader

    print(type(reader)) #reader is an instance of ABF2Reader
    print(type(MetaReader)) #MetaReader is just the metaclass, not instantiated
    print(type(ABF2Reader)) #Just the subclass, not instantiated

prints:

<class 'plugins.datareaders.ABF2Reader.ABF2Reader'>
<class 'abc.ABCMeta'>
<class 'abc.ABCMeta'>

The type for reader is what I expect: it's an instance of ABF2Reader. But the types for the other two are not. type(MetaReader) is also fine, but I expect type(ABF2Reader) to give me <class 'MetaReader'>, not <class 'abc.ABCMeta'>

Clearly I am using metaclasses wrong. Can someone shed some light on where I am screwing this up?


Solution

  • This code is not defining any new metaclasses to startwith. And the truth is that you probably don´t need custom metaclasses anyway - it would be possible to tell if you said what is your intention with the use of metaclasses. As it is, though, we just have a 2-class hierarchy that does nothing.

    So, abc.ABC, from which you inherit is a class, not a metaclass, which makes its subclasses work with the abc.abstractmethod decorator, and allow other, unrelated classes, to be registered as virtual subclasses of themselves with the myclass.register method.

    What happens is that abc.ABC itself uses a custom metaclass, the abc.ABCMeta class - and you don´t show any reason why you would want to customize it. Anyway, that is why type(MetaReader) outputs ABCMeta: its metaclass, which means, the class from which the class "MetaReader" is itself an instance, is abc.ABCMeta, through inheritance of abc.ABC.

    To apply a custom metaclass to your own classes, you´d first have to create such a metaclass, by inheriting it from type, or, in this case, from abc.ABCMeta (which inherits from type). Then, upon declaring a class, the named argument metaclass=MyMetaClass have to be used, along with the class bases:

    class MetaReader(abc.ABCMeta):
        # body which actually makes something useful on class
        # creation time, or the class behavior itself,
        # and which can´t be done by __init_subclass__
        # (usually by overriding __new__ or __call__ )    ...
    
    class ABF2Reader(abc.ABC, metaclass=MetaReader):
        ...
    

    Comlementing: after interacting with the O.P. in the comments - what they need is a way to find the superclass of the classes, not the metaclasses. The most usual way of checking that is with the issubclass call - metaclasses are not involved as described above. Onee may also want to inspect the class' __bases__ and __mro__ attributes at runtime.