from typing import Sized
from typing import Iterable
from typing import Mapping
import six


def is_listy(x):
    """Return True if `x` is "listy", i.e. a list-like object.

    "Listy" is defined as a sized iterable which is neither a map nor a string:

    >>> is_listy(["a", "b"])
    True
    >>> is_listy(set())
    True
    >>> is_listy(iter(["a", "b"]))
    False
    >>> is_listy({"a": "b"})
    False
    >>> is_listy("a regular string")
    False

    Note:
        Iterables and generators fail the "listy" test because they
        are not sized.

    Args:
        x (any value): The object to test.

    Returns:
        bool: True if `x` is "listy", False otherwise.

    """
    return (isinstance(x, Sized) and
            isinstance(x, Iterable) and
            not isinstance(x, Mapping) and
            not isinstance(x, six.string_types))