import operator
from abc import abstractmethod, ABCMeta
from typing import TypeVar, Callable, Optional, Collection, Literal, Iterable, Sequence
from .views import AncestorsView, PathView, NodesView, LeavesView, LevelsView, SiblingsView, BinaryNodesView
from .. import generics
TNode = TypeVar("TNode")
TMutDownNode = TypeVar("TMutDownNode", bound="MutableDownTree")
Order = Literal["pre", "post", "level"]
class AbstractTree(metaclass=ABCMeta):
"""Most abstract baseclass for everything."""
__slots__ = ()
@classmethod
def convert(cls, obj):
"""Convert obj to tree-type or raise TypeError if that doesn't work.
Maybe just use `as_tree(x) -> Tree` instead.
"""
from ..adapters import convert_tree
return convert_tree(obj, cls)
@property
def nid(self) -> int:
"""Unique number that represents this node."""
return id(self)
class UpTree(AbstractTree, metaclass=ABCMeta):
"""Abstract class for tree classes with parent but no children."""
__slots__ = ()
@property
@abstractmethod
def parent(self: TNode) -> Optional[TNode]:
"""Parent of this node or None if root."""
return None
@property
def is_root(self) -> bool:
"""Whether this node is a root (has no parent)."""
return self.parent is None
@property
def root(self) -> TNode:
"""Root of tree."""
p, p2 = self, self.parent
while p2:
p, p2 = p2, p2.parent
return p
@property
def ancestors(self):
"""View of ancestors of node."""
return AncestorsView(self)
@property
def path(self):
"""View of path from root to node."""
return PathView(self)
[docs]class DownTree(AbstractTree, metaclass=ABCMeta):
"""Abstract class for tree classes with children but no parent."""
__slots__ = ()
@property
@abstractmethod
def children(self: TNode) -> Collection[TNode]:
"""Children of this node."""
return ()
@property
def leaves(self):
"""View of leaves from this node."""
return LeavesView(self)
@property
def nodes(self):
"""View of this node and its descendants."""
return NodesView(self)
@property
def descendants(self):
"""View of descendants of this node."""
return NodesView(self, include_root=False)
@property
def levels(self):
"""View of this node and descendants by level."""
return LevelsView(self)
@property
def is_leaf(self) -> bool:
"""Whether this node is a leaf (does not have children)."""
return not self.children
[docs]class MutableDownTree(DownTree, metaclass=ABCMeta):
"""Abstract class for mutable tree with children."""
__slots__ = ()
[docs] @abstractmethod
def add_child(self, node: TNode):
"""Add node to children."""
raise NotImplementedError
[docs] @abstractmethod
def remove_child(self, node: TNode):
"""Remove node from children."""
raise NotImplementedError
[docs] def add_children(self, children: Iterable[TNode]):
"""Add multiple nodes to children."""
for child in children:
self.add_child(child)
[docs]class Tree(UpTree, DownTree, metaclass=ABCMeta):
"""Abstract class for tree classes with access to children and parents."""
__slots__ = ()
@property
def siblings(self):
"""View of siblings of this node."""
return SiblingsView(self)
[docs]class MutableTree(Tree, MutableDownTree, metaclass=ABCMeta):
"""Abstract class for mutable tree with children and parent."""
__slots__ = ()
[docs] def detach(self) -> TNode:
"""Remove parent if any and return self."""
if p := self.parent:
p.remove_child(self)
return self
[docs]class BinaryDownTree(DownTree, metaclass=ABCMeta):
"""Binary-tree with links to children."""
__slots__ = ()
@property
@abstractmethod
def left_child(self) -> Optional[TNode]:
return None
@property
@abstractmethod
def right_child(self) -> Optional[TNode]:
return None
@property
def children(self) -> Sequence[TNode]:
nodes = list()
if self.left_child is not None:
nodes.append(self.left_child)
if self.right_child is not None:
nodes.append(self.right_child)
return nodes
@property
def nodes(self):
return BinaryNodesView(self)
@property
def descendants(self):
return BinaryNodesView(self, include_root=False)
[docs]class BinaryTree(BinaryDownTree, Tree, metaclass=ABCMeta):
"""Binary-tree with links to children and to parent."""
__slots__ = ()
# Some optimizations
generics.children.register(DownTree, operator.attrgetter("children"))
generics.parent.register(UpTree, operator.attrgetter("parent"))
generics.nid.register(AbstractTree, operator.attrgetter("nid"))
generics.label.register(AbstractTree, str)