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.
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.