Singly Linked List in Python

I was reading about data structures this evening and I worked out simple singly linked list. The neat thing about this implementation is that a I made it iterable, also. I’d originally wanted to provide a minimal working singly linked list, and then add features and testing with explanations, but it’s been a long day. This example assumes that you’re using Python2.7; version 3 provides a __next__ class method.

Another neat thing about this implementation is that you get Stack and Queue data structures for free. You can turn this into a Stack by deleting the popLast() and addLast() methods, or Queue by deleting the popFirst() and addLast(). If you can manage with the awkward names, then you should be fine.

class SinglyLinkedList:
    
    class Node:
        def __init__( self, data, nextNode ):
            self.nextNode = nextNode
            self.data = data
        
    def __init__( self ):
        self.first = None
        self.last = None
        self.count = 0
        self.current = None
    def addFirst( self, data ):
        if self.count == 0:
            self.first = self.Node( data, None )
            self.last = self.first
        elif self.count > 0:
            self.first = self.Node( data, self.first )
        self.current= self.first
        self.count += 1
    def addLast( self, data ):
        if self.count == 0:
            self.addFirst( data )
        elif self.count > 0:
            self.last.nextNode = self.Node( data, None )
            self.last = self.last.nextNode
            self.count += 1
    def popLast( self ):
        cursor = self.first
        if self.count == 0:
            raise RuntimeError("Cannot pop from an empty linked list")
        elif self.count == 1:
            result = cursor.data
            self.first = None
            self.last = None
            self.current = None
        else:
            for i in range( self.count-2 ):
                cursor = cursor.nextNode
            result = cursor.nextNode.data
            self.last = cursor
            self.last.nextNode = None
        self.count -= 1
        return result
    def popFirst( self ):
        if self.count == 0:
            raise RuntimeError("Cannot pop from an empty linked list")
        result = self.first.data
        if self.count == 1:
            self.first = None
            self.last = None
        else:
            self.first = self.first.nextNode
        self.current = self.first
        self.count -= 1
        return result
    def __repr__( self ):
        result = ""
        if self.count == 0:
            return "..."
        cursor = self.first
        for i in range( self.count ):
            result += "{}".format(cursor.data)
            cursor = cursor.nextNode
            if cursor is not None:
                result += " -> "
        return result
    def __iter__( self ):
        # lets Python know this class is iterable
        return self
    def next( self ):
        # provides things iterating over class with next element
        if self.current is None:
            # allows us to re-iterate
            self.current = self.first
            raise StopIteration
        else:
            result = self.current.data
            self.current = self.current.nextNode
            return result
    def __len__( self ):
        return self.count

sll = SinglyLinkedList()
sll.addLast("!!")
sll.addFirst("pls")
sll.addFirst("me")
sll.addFirst("halp")
sll.addLast("!!!")

assert sll.count > 0
assert len( sll ) == 5
assert list( sll ) == ["halp","me","pls","!!", "!!!"]
assert sll.popLast() == "!!!"
assert list( sll ) == ["halp","me","pls","!!"]