def getargspec(obj):
    """An improved inspect.getargspec.

    Has a slightly different return value from the default getargspec.

    Returns a tuple of:
        required, optional, args, kwargs
        list, dict, bool, bool

    Required is a list of required named arguments.
    Optional is a dictionary mapping optional arguments to defaults.
    Args and kwargs are True for the respective unlimited argument type.
    """

    argnames, varargs, varkw, _defaults = None, None, None, None

    if inspect.isfunction(obj) or inspect.ismethod(obj):
        argnames, varargs, varkw, _defaults = inspect.getargspec(obj)

    elif inspect.isclass(obj):
        if inspect.ismethoddescriptor(obj.__init__):
            argnames, varargs, varkw, _defaults = [], False, False, None

        else:
            argnames, varargs, varkw, _defaults = inspect.getargspec(obj.__init__)

    elif hasattr(obj, '__call__'):
        argnames, varargs, varkw, _defaults = inspect.getargspec(obj.__call__)

    else:
        raise TypeError("Object not callable?")

    # Need test case to prove this is even possible.
    # if (argnames, varargs, varkw, defaults) is (None, None, None, None):
    #     raise InspectionFailed()

    if argnames and argnames[0] == 'self':
        del argnames[0]

    if _defaults is None:
        _defaults = []
        defaults = dict()

    else:
        # Create a mapping dictionary of defaults; this is slightly more useful.
        defaults = dict()
        _defaults = list(_defaults)
        _defaults.reverse()
        argnames.reverse()
        for i, default in enumerate(_defaults):
            defaults[argnames[i]] = default

        argnames.reverse()
        # del argnames[-len(_defaults):]

    return argnames, defaults, True if varargs else False, True if varkw else False