a loop is when the exact same node appears more than once in a linked list rootnode node1 rootnode nextnode node2 rootnode nextnode nextnode node3 rootnode nextnode nextnode nextnode node4 rootnode hasloop false rootnode nextnode nextnode nextnode rootnode nextnode rootnode hasloop true a loop is when the exact same node appears more than once in a linked list root_node node 1 root_node next_node node 2 root_node next_node next_node node 3 root_node next_node next_node next_node node 4 root_node has_loop false root_node next_node next_node next_node root_node next_node root_node has_loop true false true false false
from __future__ import annotations

from typing import Any


class ContainsLoopError(Exception):
    pass


class Node:
    def __init__(self, data: Any) -> None:
        self.data: Any = data
        self.next_node: Node | None = None

    def __iter__(self):
        node = self
        visited = []
        while node:
            if node in visited:
                raise ContainsLoopError
            visited.append(node)
            yield node.data
            node = node.next_node

    @property
    def has_loop(self) -> bool:
        
        try:
            list(self)
            return False
        except ContainsLoopError:
            return True


if __name__ == "__main__":
    root_node = Node(1)
    root_node.next_node = Node(2)
    root_node.next_node.next_node = Node(3)
    root_node.next_node.next_node.next_node = Node(4)
    print(root_node.has_loop)  
    root_node.next_node.next_node.next_node = root_node.next_node
    print(root_node.has_loop)  

    root_node = Node(5)
    root_node.next_node = Node(6)
    root_node.next_node.next_node = Node(5)
    root_node.next_node.next_node.next_node = Node(6)
    print(root_node.has_loop)  

    root_node = Node(1)
    print(root_node.has_loop)