is_iterable function.

Neil Cerutti horpner at yahoo.com
Thu Jul 26 14:56:46 EDT 2007


On 2007-07-26, Marc 'BlackJack' Rintsch <bj_666 at gmx.net> wrote:
> On Thu, 26 Jul 2007 15:02:39 +0000, Neil Cerutti wrote:
>
>> Based on the discussions in this thread (thanks all for your
>> thoughts), I'm settling for:
>> 
>> def is_iterable(obj):
>>   try:
>>     iter(obj).next()
>>     return True
>>   except TypeError:
>>     return False
>>   except KeyError:
>>     return False
>> 
>> The call to iter will fail for objects that don't support the
>> iterator protocol, and the call to next will fail for a
>> (hopefully large) subset of the objects that don't support the
>> sequence protocol.
>
> And the `next()` consumes an element if `obj` is not
> "re-iterable".

Crap.

So how best to imlement deeply_mapped?

The following still breaks for objects that don't provide
__iter__, do provide __getitem__, but don't support the sequence
protocol.

def deeply_mapped(func, iterable):
    """ Recursively apply a function to every item in a nested container,
    recursively descending into items that are iterable. The result is an
    iterator over the mapped values. Similar to the builtin map function, func
    may be None, causing the items to returned unchanged.

    >>> import functools
    >>> flattened = functools.partial(deeply_mapped, None)
    >>> list(flattened([[1], [2, 3, []], 4]))
    [1, 2, 3, 4]

    >>> list(flattened(((1), (2, 3, ()), 4)))
    [1, 2, 3, 4]

    >>> list(flattened([[[[]]], 1, 2, 3, 4]))
    [1, 2, 3, 4]

    >>> list(flattened([1, [[[2, 3]], 4]]))
    [1, 2, 3, 4]

    >>> def magic(o):
    ...     if o == 3:
    ...         raise TypeError('Three is a magic number')
    ...     return o

    >>> list(deeply_mapped(magic, [1, [[[2, 3]], 4]]))
    Traceback (most recent call last):
        ...
    TypeError: Three is a magic number

    """
    frame = inspect.currentframe()
    info = inspect.getframeinfo(frame)
    filename = info[0]
    funcname = info[2]
    for item in iterable:
        try:
            for it in deeply_mapped(func, item):
                if func is None:
                    yield it
                else:
                    yield func(it)
        except TypeError:
            eframe = inspect.trace()[-1]
            efilename = eframe[1]
            efuncname = eframe[3]
            if efilename != filename or efuncname != funcname:
                raise
            if func is None:
                yield item
            else:
                yield func(item)

-- 
Neil Cerutti



More information about the Python-list mailing list