I recently have been working on a BinaryTree
class, and looked into creating a proper __repr__
which when eval'ed would recreate the exact binary tree as the original.
The following code for __repr__
works as expected, but I'm wondering if there is a better way to handle the removal of values which already has a default parameter in the __init__
method of the class. That is, avoiding to use replace()
to remove the None
cases at the end of __repr__
.
class BinaryTree(object):
"""Structure to hold a binary tree."""
def __init__(self, value, left = None, right = None):
self.value = value
self.left = left
self.right = right
def __repr__(self):
"""Return a string which when eval'ed will rebuild tree"""
return '{}({}, {}, {})'.format(
self.__class__.__name__,
repr(self.value),
repr(self.left) if self.left else None,
repr(self.right) if self.right else None) \
.replace(', None, None)', ')') \
.replace(', None)', ')')
def __eq__(self, other):
"""Check if both trees exists, and have same value and subtrees."""
# Return early if only one of the tree exists
# or if only one of them has a right tree
# or if only one of them has a left tree
if (bool(self) ^ bool(other)
or (bool(self.right) ^ bool(other.right))
or (bool(self.left) ^ bool(other.left))):
return False
# Otherwise compare the values, and that sub trees are equal
return (self.value == other.value
and self.right == other.right
and self.left == other.left)
def __ne__(self, other):
"""Negated version of BinaryTree.__eq__"""
return not self.__eq__(other)
Some test code verifying that it actually works:
trees = []
trees.append(('balanced manual', BinaryTree('D', BinaryTree('B', BinaryTree('A'), BinaryTree('C')),
BinaryTree('F', BinaryTree('E')))))
trees.append(('alphalevel manual', BinaryTree('A', BinaryTree('B', BinaryTree('D'), BinaryTree('E')), BinaryTree('C', BinaryTree('F')))))
trees.append(('integer_tree manual', BinaryTree(4, BinaryTree(2, BinaryTree(1), BinaryTree(3)), BinaryTree(6, BinaryTree(5)))))
trees.append(('strange_tree manual',
BinaryTree('G',
BinaryTree('C',
BinaryTree('B',
BinaryTree('A')),
BinaryTree('F',
BinaryTree('D',
None,
BinaryTree('E')))),
BinaryTree('H',
None,
BinaryTree('I')))))
for tree_name, tree in trees:
print('Treename: {}, equal to __repr__ version: {}'.format(tree_name,
tree == eval(repr(tree))))
I do know that it doesn't hurt to explicitly specify the default parameters when eval'ing the repr version, but I'm contemplating on using some the repr-versions in tests and so on, and the version without None
looks a little nicer than the one with:
BinaryTree('D', BinaryTree('B', BinaryTree('A'), BinaryTree('C')), BinaryTree('F', BinaryTree('E')))
BinaryTree('D', BinaryTree('B', BinaryTree('A', None, None), BinaryTree('C', None, None)), BinaryTree('F', BinaryTree('E', None, None), None))
None
at all? – Barry 2 days ago