from copy import copy
from collections import MutableSequence

class LinkedList:
    """Linked list class"""

    def __init__(self: 'LinkedList', item: object=None,
                 rest: 'LinkedList'=None) -> None:
        """Create a new LinkedList
        item - The first element of the linked list, 
        Not present if self will be the empty linked list.
        rest - The linked list that contains the elements after item. 
        Not present if self will be the empty linked list.""" 
        self.empty = (item is None) and (rest is None)
        if not self.empty:
            self.item = item
            if rest is None:
                self.rest = LinkedList()
            else:
                self.rest = rest

    def prepend(self: 'LinkedList', item: object) -> None:
        """Add new item to front of self

        >>> lnk = LinkedList()
        >>> lnk.prepend(7)
        >>> lnk.item == 7
        True
        """
        self.item, self.rest, self.empty = item, copy(self), False

    def __repr__(self: 'LinkedList') -> str:
        """Return str representation of LinkedList self"""
        if self.empty:
            return 'LinkedList()'
        else:
            return 'LinkedList({}, {})'.format(
                repr(self.item), repr(self.rest))

    def decapitate(self: 'LinkedList') -> None:
        """Delete item from LinkedList self.

        >>> lnk = LinkedList(5, LinkedList())
        >>> lnk.prepend(7)
        >>> lnk.decapitate()
        >>> repr(lnk) == 'LinkedList(5, LinkedList())'
        True
        """
        if self.empty:
            raise Exception("Can't decapitate empty list!")
        elif not self.rest.empty:
            self.item, self.rest = self.rest.item, self.rest.rest
        else:
            self.empty = True
            del(self.item)
            del(self.rest)

    def __contains__(self: 'LinkedList', value: object) -> bool:
        """Return whether self LinkedList contains value

        >>> lnk = LinkedList(5, LinkedList(7, LinkedList()))
        >>> 13 in lnk
        False
        >>> 7 in lnk
        True
        """
        return (not self.empty) and (self.item == value or value in self.rest)

    def __getitem__(self: 'LinkedList', ind: int) -> object:
        """Return the item at position ind.
        Raise exception if ind is out-of-range

        >>> lnk = LinkedList(5, LinkedList(7, LinkedList(11, LinkedList())))
        >>> lnk[1]
        7
        """
        if self.empty or ind < 0:
            raise IndexError
        elif ind > 0:
            return self.rest.__getitem__(ind - 1)
        else:
            return self.item

if __name__ == '__main__':
    import doctest
    doctest.testmod()
