1

Python 3.6 added PEP 487, which adds an __init_subclass__ method among other things. Is it possible to write a version of ABC that doesn't use a metaclass?

Neil G
  • 32,138
  • 39
  • 156
  • 257
  • If all you care about is the check for abstract methods, then yes. But abcs also support [virtual subclass registration](https://docs.python.org/3/library/abc.html#abc.ABCMeta.register). – Martijn Pieters Jul 07 '17 at 18:09
  • @MartijnPieters Yes, I meant to specify that all I care about is the check for abstract methods. – Neil G Jul 07 '17 at 18:11
  • Is this about writing an alternative to the standard library `ABC`, or about how to use the existing `ABC`? – Håken Lid Jul 07 '17 at 18:15
  • 1
    @HåkenLid An alternative. The problem with metaclasses in Python is that as soon as I have more than one in my inheritance tree, I have to start composing the metaclasses, which is annoying. However, I would like to use the abstractmethod functionality. – Neil G Jul 07 '17 at 18:16

1 Answers1

3

If all you care about is the check for abstract methods, then yes. Just move the abstract method set computation to the __init_subclass__ method:

def __init_subclass__(cls, **kwargs):
    super().__init_subclass__(**kwargs)
    # Compute set of abstract method names
    abstracts = {name
                 for name, value in vars(cls).items()
                 if getattr(value, "__isabstractmethod__", False)}
    for base in cls.__bases__:
        for name in getattr(base, "__abstractmethods__", set()):
            value = getattr(cls, name, None)
            if getattr(value, "__isabstractmethod__", False):
                abstracts.add(name)
    cls.__abstractmethods__ = frozenset(abstracts)

The base object.__new__ implementation then uses an non-empty __abstractmethods__ set to prevent instantiation.

But ABCs also support virtual subclass registration; the two hook methods this requires have to be implemented on the metaclass.

flornquake
  • 3,156
  • 1
  • 21
  • 32
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343