Implement LinkedList class in python
All of your methods on Node are useless. Just use Node.val and Node.next like you already are.
insert_after_key is likely to insert the data multiple times if there are multiple keys. Given how insert_before_key works differently you should test you code with unittests.
Your code fails silently. I don't recommend this.
You can remove the looping from all your functions if you add add a _iter function.
You can remove the need to loop to fine the key in most of your functions if you add a _find_key function, which returns the previous and next node.
I'd implement (5) using pairwise (itertools recipe) and use the _iter function.
You can simplify append if you use the tail itertools recipe.
You don't seem to have much code to handle empty lists.




import collections
import itertools


def pairwise(iterable):
    "s -&gt; (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)


def tail(n, iterable):
    "Return an iterator over the last n items"
    # tail(3, 'ABCDEFG') --&gt; E F G
    return iter(collections.deque(iterable, maxlen=n))


class Node:
    def __init__(self, data, next=None):
        self.val = data
        self.next = next


class SingleLinkedList(object):
    def __init__(self, data):
        self.head = data

    def __repr__(self):
        return '-&gt;'.join(str(n) for n in self._iter())

    def __len__(self):
        return sum(1 for _ in self._iter())

    def _iter(self):
        node = self.head
        while node is not None:
            yield node
            node = node.next

    def append(self, data):
        if not self.head:
            self.head = Node(data)
            return

        last, = tail(self._iter(), 1)
        last.next = Node(data)

    def _find_key(self, key):
        if self.head is None:
            raise IndexError('Key not found')
        if key == self.head.value:
            return None, self.head

        for prev, curr in pairwise(self._iter()):
            if curr.val == key:
                return prev, curr
        raise IndexError('Key not found')

    def insert_before_key(self, key, data):
        prev, curr = self._find_key(key)
        if prev is None:
            self.head = Node(data, self.head)
        else:
            prev.next = Node(data, curr)

    def insert_after_key(self, key, data):
        _, node = self._find_key(key)
        node.next = Node(data, node.next)

    def delete(self, key):
        prev, curr = _find_key(key)
        if prev is None:
            self.head = curr.next
        else:
            prev.next = curr.next

    def search(self, key):
        _, node = _find_key(key)
        return node

    def reverse(self):
        cur = self.head
        prev = None
        while cur:
            nxt = cur.next
            cur.next = prev
            prev = cur
            cur = nxt
        self.head = prev