2

Say I make a class named Bird which I only want to be used as a parent class and derived classes are expected to have a method flap_wings:

class Bird:

    def fly(self):
        self.flap_wings()

An expected derived class might look like:

class Eagle(Bird):

    def flap_wings(self):
        print('flapping wings')

What is a nice, clear way for Bird to both assert that its derived classes have the method flap_wings as well as include documentation on what flap_wings is expected to do?

Right now, I'm using __init_subclass__:

class Bird:

    def fly(self):
        self.flap_wings()

    def __init_subclass__(cls, **kwargs):
        assert hasattr(cls, 'flap_wings'), (
            "Derived classes have to have a flap_wings method which should "
            "print 'flapping wings'."
        )

But, the assert expression only shows up after you create a Bird class and is not a "real" docstring that can be accessed through help.

I know this is an open ended question but what are other better ways? It's not against the rules to define flap_wings within Bird first, maybe just with the body pass and a docstring. But I just couldn't find the "standard" ways to handle this situation. So I'm looking for any suggestions.

van neilsen
  • 547
  • 8
  • 21
Jay Calamari
  • 573
  • 4
  • 17
  • Thanks for the references to the previous questions. I was using `__init_subclass__` with the impression that it was a simpler way to do what metaclasses did here, but I guess `__init_subclass__` just doesn't offer enough to do what I want. – Jay Calamari Jun 21 '18 at 17:44

1 Answers1

2

You can use the abc library for abstract methods:

from abc import ABCMeta, abstractmethod
import six

class Bird(six.with_metaclass(ABCMeta)):
    def fly(self):
        """Take flight.

        Notes
        -----
        This depends on the abstract method `flap_wings`. If you've
        not implemented this at the subclass level, your subclass
        cannot be properly instantiated.
        """
        self.flap_wings()

    @abstractmethod
    def flap_wings(self):
        """Subclasses must implement this"""

This establishes a contract of sorts. Any subclass that does NOT implement the flap_wings method will raise an error on instantiation:

class Flamingo(Bird):
    pass

>>> Flamingo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Flamingo with abstract methods flap_wings

Whereas a sub-class that implements the abstract method will work fine:

class BlueJay(Bird):
    def flap_wings(self):
        print("Flappity flap")

>>> BlueJay().fly()
Flappity flap

As far as documenting the subclass, since all subclasses inherit the fly method, you could include in its docstring that it calls the flap_wings method and expects it to be present.

TayTay
  • 6,882
  • 4
  • 44
  • 65
  • 1
    Thanks for this, especially showing the organization of the docstrings. And good choice of birds. I'll use metaclasses. – Jay Calamari Jun 21 '18 at 17:44