根据定义,抽象类是你不能直接实例化的;相反,您实例化实现它的具体类(通常是抽象类的子类)。
所以,为你的抽象类定义你想要的编程接口:例如,在 Python 2 中(它在 Py 3 中更优雅一点,但等效):
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
然后是一个或多个具体的子类,并使用后者。
例如:
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
请注意,这是一个非常反常的情况,因为具体类没有从那个空的抽象类中获得任何功能——在现实生活中,即除了教学目的之外,我不会不为此使用抽象类案例,因为这里没有附加值。不过,如果你坚持,这就是你的做法:-)。
对于您指定的辅助功能也是如此(这里我将切换到 Py 3,因为优雅是不可抗拒的):
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)
在现实生活中,我会将此作为 AbstractTree 的方法,不是您需要的独立函数。同样对于您指定的其他三个辅助函数,它们与抽象类的 getter 方法完全相同...!
编辑:显然,形容词 abstract 已经足够重载,以至于 Q 不是关于 Python 自己的抽象基类,而是更抽象(!)的感觉“抽象”这个词。在这种情况下,例如,可以将每个树节点映射到一个 3 项元组(如果树节点是不可变的):
(entry, left, right)
仍然使用None 来表示“失踪”的left 和right 孩子;在这种情况下,访问函数显然是
def entry(tree): return tree[0]
def left_branch(tree): return tree[1]
def right_branch(tree): return tree[2]
并且请求的在树上按顺序遍历的函数将使用生成器,例如(回到与 Py2 兼容的代码:-)...:
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
我仍在使用生成器,因为它显然是走一棵树的“唯一正确的方法”(可以选择如何处理每个条目,无论是 printing 还是其他方式,显然属于在这个生成器上循环的不同函数!)。但是我已经切换到 yield an_entry 的 Py2 兼容循环,因为如果作业需要,用 print 替换每个 yield 是微不足道的。
我更改了 Q 规范中的名称,因为 Q 的规范完全破坏了 WRT 命名——它们要求将本地参数(第一个参数)命名为 entry 但也树节点条目的访问器函数完全相同命名为entry——导致完全不必要的命名冲突[*]。所以我只保留了 entry 名称作为访问器函数,并切换到 the_entry 作为参数名称和 an_entry 作为 for 循环中所需的其他局部变量。
[*] 它可以解决,例如通过使用globals()['entry'] 来访问阴影全局名称,但是当只使用非- 首先冲突的名称要简单得多!-)