On Jul 26, 2:56 pm, Neil Cerutti <[EMAIL PROTECTED]> wrote: > On 2007-07-26, Marc 'BlackJack' Rintsch <[EMAIL PROTECTED]> 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.
That's not the only problem; try a string element to see it break too. More importantly, do you *always* want to handle strings as iterables ? The best general way to do what you're trying to is pass is_iterable() as an optional argument with a sensible default, but allow the user to pass a different one that is more appropriate for the task at hand: def is_iterable(obj): try: iter(obj) except: return False else: return True def flatten(obj, is_iterable=is_iterable): if is_iterable(obj): for item in obj: for flattened in flatten(item, is_iterable): yield flattened else: yield obj from functools import partial flatten_nostr = partial(flatten, is_iterable=lambda obj: not isinstance(obj,basestring) and is_iterable(obj)) print list(flatten_nostr([1, [[[2, 'hello']], (4, u'world')]])) By the way, it's bad design to couple two distinct tasks: flattening a (possibly nested) iterable and applying a function to its elements. Once you have a flatten() function, deeply_mapped is reduced down to itertools.imap. HTH, George -- http://mail.python.org/mailman/listinfo/python-list