Advertisement
Guest User

Untitled

a guest
Oct 15th, 2019
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.74 KB | None | 0 0
  1. from decimal import Decimal
  2.  
  3.  
  4. class AccountNode:
  5. """
  6. A general tree node representing one account.
  7. """
  8. def __init__(self, key, value=None):
  9. self.key = key
  10. # The Decimal class provides accurate storage and
  11. # arithmetics of floating point
  12. # numbers without rounding errors.
  13. self.value = Decimal(str(value)) if value else None
  14. self.left_child = None
  15. self.right_sibling = None
  16.  
  17. def height(self):
  18. """
  19. Return the height of self by calculating the height of its children.
  20. """
  21. if self.left_child is None:
  22. return 0
  23. if self.left_child.right_sibling is None:
  24. return 1 + self.left_child.height()
  25. return 1 + max(self.left_child.height(), self.left_child.right_sibling.height())
  26.  
  27. def __repr__(self):
  28. if self.value:
  29. return "<AccountNode: key={0}, value={1:.2f}>".format(self.key, self.value)
  30. else:
  31. return "<AccountNode: key={0}, value=None>".format(self.key)
  32.  
  33.  
  34. class AccountTree:
  35. """
  36. A general tree representing accounts and subaccounts using AccountNode objects.
  37. """
  38. def __init__(self):
  39. self.root = None
  40.  
  41. def insert(self, new_key, new_value, parent_key=None, left_sibling_key=None):
  42. """
  43. Insert a new node with new_key and value as the child of the node with key
  44. parent_key and as the right_sibling of the node with the key left_sibling_key.
  45. """
  46. if self.find(new_key) is not None:
  47. raise RuntimeError("Account with key {} already exists.".format(new_key))
  48.  
  49. new_node = AccountNode(new_key, new_value)
  50.  
  51. if parent_key is None:
  52. # Insert as the new root
  53. old_root, self.root = self.root, new_node
  54. new_node.left_child = old_root
  55. return new_node
  56.  
  57. if left_sibling_key is None:
  58. # Insert as the new left child of node with parent_key
  59. parent_node = self[parent_key]
  60. old_left = parent_node.left_child
  61. new_node.right_sibling = old_left
  62. parent_node.left_child = new_node
  63. return new_node
  64.  
  65. # Insert as the child of node with key parent_key and between
  66. # the node with key left_sibling_key and its right sibling
  67. parent_node = self[parent_key]
  68. left_sibling = self.find(left_sibling_key, parent_node)
  69. if left_sibling is None:
  70. raise KeyError("Node with key {0} is not a child of a node with key {1}.".format(left_sibling_key, parent_key))
  71. old_right = left_sibling.right_sibling
  72. left_sibling.right_sibling = new_node
  73. new_node.right_sibling = old_right
  74.  
  75. return new_node
  76.  
  77. def find(self, key, root=None):
  78. """
  79. Return node with key or None if a matching node is not found.
  80. If root is None start searching at self.root, else start searching
  81. at root.
  82. """
  83. if root is None:
  84. root = self.root
  85. for node in self._visit_preorder(root):
  86. if node.key == key:
  87. return node
  88. return None
  89.  
  90. def clear_totals(self):
  91. """
  92. Set the 'value' attribute of each internal node to None.
  93. """
  94. for node in self._visit_preorder(self.root):
  95. if node.left_child:
  96. node.value = None
  97.  
  98. def update_totals(self):
  99. """
  100. Set the 'value' attribute of each internal node to the sum of its
  101. childrens' values.
  102. The sum is represented as a Decimal instance.
  103. """
  104. def total(node):
  105. if node is None:
  106. return Decimal('0')
  107. node_value = node.value if node.value is not None else Decimal('0')
  108. return node_value + total(node.right_sibling)
  109.  
  110. for node in self._visit_postorder(self.root):
  111. if node.value is not None:
  112. continue
  113. elif node.left_child is None:
  114. raise RuntimeError(
  115. "The AccountTree should not contain leafs with None values." +
  116. "The node at key {0!r} has a value of None.".format(node.key)
  117. )
  118. else:
  119. node.value = total(node.left_child)
  120.  
  121.  
  122. # Implement the traversal methods below
  123.  
  124. # Exercise 5.2.6
  125. def _visit_preorder(self, node):
  126. """
  127. Return a list of nodes in preorder starting from node.
  128. """
  129. if node is None:
  130. node = self.root
  131. if self.root is None:
  132. return []
  133. else:
  134. self._visit_preorder(self.root)
  135. if node.left_child is None and node.right_sibling is None:
  136. return node.value
  137. else:
  138. res = "(" + node.value + self._visit_preorder(node.left_child) + self._visit_preorder(
  139. node.right_sibling) + ")"
  140. return res
  141.  
  142.  
  143.  
  144. # Exercise 5.2.7
  145. # NOTE: You don't need to implement _visit_postorder to get full points
  146. # from exercise 5.2.6.
  147. # If you remove or comment out the NotImplementedError,
  148. # the local tests will assume you have implemented this method
  149. # and runs all tests related to this method.
  150. def _visit_postorder(self, node):
  151.  
  152. """
  153.  
  154. Return a list of nodes in postorder starting from node.
  155.  
  156. """
  157.  
  158. if node is None:
  159.  
  160. return
  161.  
  162. raise NotImplementedError("_visit_postorder not implemented")
  163.  
  164. def __getitem__(self, key):
  165. """
  166. Get accounts by key, e.g. tree[key].
  167. """
  168. node = self.find(key)
  169. if node is None:
  170. raise KeyError("No account with id {}".format(key))
  171. return node
  172.  
  173. def _traverse(self, iterator):
  174. if self.root is None:
  175. return
  176. for node in iterator(self.root):
  177. yield (node.key, node.value)
  178.  
  179. def _str(self, iterator):
  180. report_string = "{0} {1:>7}\n".format("Account", "Value")
  181. report_string += (len(report_string) + 1)*'-'
  182.  
  183. for key, value in iterator:
  184. report_string += '\n'
  185. if value is None:
  186. report_string += "{0} {1:>10}".format(key, "-")
  187. else:
  188. report_string += "{0} {1:+12.2f}".format(key, value)
  189.  
  190. return report_string
  191.  
  192. def view_str(self):
  193. """
  194. Return a printable table of this tree.
  195. """
  196. return self._str(self._traverse(self._visit_preorder))
  197.  
  198. def report_str(self):
  199. """
  200. Return a printable table of the financial report of this tree.
  201. """
  202. self.clear_totals()
  203. self.update_totals()
  204. return self._str(self._traverse(self._visit_postorder))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement