Groups an iterable by a specified attribute, or using a specified key access function.  Returns tuples of grouped elements.

    >>> dogs = [Dog('gatsby', 'Rruff!', 15), Dog('william', 'roof', 12), Dog('edward', 'hi', 15)]
    >>> groupby(dogs, 'weight')
    [(Dog('gatsby', 'Rruff!', 15), Dog('edward', 'hi', 15)), (Dog('william', 'roof', 12), )]

    :param iterable: iterable to be grouped
    :param key: a key-access function or attr name to be used as a group key

def group(iterable, key=lambda ele: ele):
    """ Groups an iterable by a specified attribute, or using a specified key access function.  Returns tuples of grouped elements.

    >>> dogs = [Dog('gatsby', 'Rruff!', 15), Dog('william', 'roof', 12), Dog('edward', 'hi', 15)]
    >>> groupby(dogs, 'weight')
    [(Dog('gatsby', 'Rruff!', 15), Dog('edward', 'hi', 15)), (Dog('william', 'roof', 12), )]

    :param iterable: iterable to be grouped
    :param key: a key-access function or attr name to be used as a group key
    """
    if callable(key):
        return _group(iterable, key)
    else:
        return _group(iterable, make_key_fn(key))