By definition, an abstract class is one you cannot directly instantiate; rather, you instantiate concrete classes (usually subclasses of the abstract one) implementing it.
So, define the programming interface you want for your abstract class: e.g, in Python 2 (it's a bit more elegant in Py 3, but equivalent):
import abc
class AbstractTree(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def entry(self): return
@abc.abstractmethod
def left(self): return
@abc.abstractmethod
def right(self): return
and then one or more concrete subclasses, and use the latter.
For example:
class SimpleTree(AbstractTree):
def __init__(self, entry, left=None, right=None):
self.entry = entry
self.left = left
self.right = right
def entry(self): return self.entry
def left(self): return self.left
def right(self): return self.right
Note that this is quite an anomalous case since the concrete class gets no functionality from that empty abstract class -- in real life, i.e except for didactic purposes, I would not use abstract classes for this case, as there's no added value in them here. Still, if you insist, this is how you do it:-).
Similarly for the auxiliary function you specify (here I'm switching to Py 3 as the elegance's irresistible):
def binary_tree_generator(entry, left, right):
if left is not None:
yield from binary_tree_generator(left.entry, left.left, left.right)
yield entry
if right is not None:
yield from binary_tree_generator(right.entry, right.left, right.right)
def binary_tree(entry, left, right):
for anentry in binary_tree_generator(entry, left, right):
print(anentry)
In real life, I'd make this a method of AbstractTree, not a free-standing function as you've required. Similarly for the other three auxiliary functions you specify, which exactly duplicate the getter methods of the abstract class...!
EDIT: apparently the adjective abstract is overloaded enough that the Q is not about Python's own abstract base classes, but a more, well, abstract (!) sense of the word "abstract". In that case, one might e.g map each tree node into a 3-items tuple (if tree nodes are immutable):
(entry, left, right)
still using None
to represent "missing" left
and right
children; in that case, the accessor functions would obviously be
def entry(tree): return tree[0]
def left_branch(tree): return tree[1]
def right_branch(tree): return tree[2]
and the requested function that does an in-order walk on the tree would use a generator such as (going back to Py2-compatible code:-)...:
def binary_tree_generator(the_entry, left, right):
if left is not None:
for an_entry in binary_tree_generator(
entry(left), left_branch(left), right_branch(left)):
yield an_entry
yield the_entry
if right is not None:
for an_entry in binary_tree_generator(
entry(right), left_branch(right), right_branch(right)):
yield an_entry
I'm still using a generator because it is so obviously the "only one right way to do it" for walking a tree (with the choice of what to do with each entry, whether print
ing it or otherwise, clearly belonging to a different function looping over this generator!). But I've switched to Py2-compatible loops of yield an_entry
as that makes it trivial to replace each yield
with a print
if required by the homework.
I've changed the names from the Q's specs because the Q's specs are totally broken WRT naming -- they require that a local argument (the first one) be named entry
but also the accessor function for the entry of a tree node be identically named entry
-- causing a completely unnecessary naming conflict [*]. So I've kept the entry
name only the for accessor function and switched to the_entry
for argument name and an_entry
for the other local variable needed in the for
loops.
[*] it could be solved, e.g by using globals()['entry']
to access the shadowed global name, but it's just silly to put oneself deliberately in the position to have to reach for such "Hail Mary passes" when just using non-conflicting names in the first place is so much simpler!-)