I wanted to extend karin's answer here where she uses a for loop for loop to print the linked list.
I would like to have __str__ method that is part of SinglyLinkedList but would like to reuse the __iter__ method as it is without the except clause which comes with StopIteration given that the answers here say that try statements are cheap except when catching the exception. Is there anyway to go about it? Is it even possible? If I understand this correctly, the for loop calls __iter__ so it would run into the same StopIteration.
The code that I have that works is here.
from typing import Optional
# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
    def __str__(self):
        return f"Node:{self.val} Next:{self.next.val if self.next else 'None'}\n"
class SinglyLinkedList():
    def __init__(self):
        self.head = None
        self.tail = None
    def __iter__(self):
        node = self.head
        
        while node:
            yield node 
            node = node.next
    def add(self, node):
        if not self.head:
            self.head = node 
        else:
            self.tail.next = node
        
        self.tail = node
    def __str__(self):
        iterator = self.__iter__()
        values = []
        try: 
            while True:
                values.append( str(next(iterator).val) )
        except StopIteration:
            pass
        return "->".join(values)
one = ListNode(1)
two = ListNode(2)
three = ListNode(3)
four = ListNode(4)
five = ListNode(5)
ll = SinglyLinkedList()
ll.add(one)
ll.add(two)
ll.add(three)
ll.add(four)
ll.add(five)
print(ll)
which gives me the output that I would like.
1->2->3->4->5
The other way I could do think of is to reuse code a while loop
def __str__(self):
        values = []
        node = self.head
        while node:
            values.append( str(node.val) )
            node = node.next
        return "->".join(values)
 
    