is_iterable function.

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Neil Cerutti

    #1

    is_iterable function.

    def is_iterable(obj ):
    try:
    iter(obj)
    return True
    except TypeError:
    return False

    Is there a better way?

    --
    Neil Cerutti
  • Duncan Booth

    #2
    Re: is_iterable function.

    Neil Cerutti <horpner@yahoo. comwrote:
    Speaking of the iter builtin function, is there an example of the
    use of the optional sentinel object somewhere I could see?
    for line in iter(open('some file.txt', 'r').readline, ''):
    print line

    Comment

    • Neil Cerutti

      #3
      Re: is_iterable function.

      On 2007-07-25, Marc 'BlackJack' Rintsch <bj_666@gmx.net wrote:
      So there's no reliable way to test for "iterables" other than
      actually iterate over the object.
      A TypeError exception is perhaps too generic for comfort in this
      use case:

      def deeply_mapped(f unc, iterable):
      for item in iterable:
      try:
      for it in flattened(item) :
      yield func(it)
      except TypeError:
      yield func(item)

      I'd be more confortable excepting some sort of IterationError (or
      using an is_iterable function, of course). I guess there's always
      itertools. ;)

      --
      Neil Cerutti

      Comment

      • Carsten Haese

        #4
        Re: is_iterable function.

        On Wed, 2007-07-25 at 19:26 +0000, Neil Cerutti wrote:
        Speaking of the iter builtin function, is there an example of the
        use of the optional sentinel object somewhere I could see?
        Example 1: If you use a DB-API module that doesn't support direct cursor
        iteration with "for row in cursor", you can simulate it this way:

        for row in iter(cursor.fet chone, None):
        # do something

        Example 2: Reading a web page in chunks of 8kB:

        f = urllib.urlopen( url)
        for chunk in iter(lambda:f.r ead(8192), ""):
        # do something

        HTH,

        --
        Carsten Haese



        Comment

        • Neil Cerutti

          #5
          Re: is_iterable function.

          On 2007-07-25, Carsten Haese <carsten@uniqsy s.comwrote:
          On Wed, 2007-07-25 at 19:26 +0000, Neil Cerutti wrote:
          >Speaking of the iter builtin function, is there an example of the
          >use of the optional sentinel object somewhere I could see?
          >
          Example 1: If you use a DB-API module that doesn't support direct cursor
          iteration with "for row in cursor", you can simulate it this way:
          >
          for row in iter(cursor.fet chone, None):
          # do something
          >
          Example 2: Reading a web page in chunks of 8kB:
          >
          f = urllib.urlopen( url)
          for chunk in iter(lambda:f.r ead(8192), ""):
          # do something
          Ah! Thanks for the examples. That's much simpler than I was
          imagining. It's also somewhat evil, but I suppose it conserves a
          global name to do it that way.

          --
          Neil Cerutti

          Comment

          • George Sakkis

            #6
            Re: is_iterable function.

            On Jul 25, 3:26 pm, Neil Cerutti <horp...@yahoo. comwrote:
            Speaking of the iter builtin function, is there an example of the
            use of the optional sentinel object somewhere I could see?
            # iterate over random numbers from 1 to 10; use 0 as a sentinel to
            stop the iteration
            for n in iter(lambda:ran dom.randrange(1 0), 0):
            print n

            More generally, iter(callable, sentinel) is just a convenience
            function for the following generator:

            def iter(callable, sentinel):
            while True:
            c = callable()
            if c == sentinel: break
            yield c


            George

            Comment

            • Steve Holden

              #7
              Re: is_iterable function.

              Carsten Haese wrote:
              On Wed, 2007-07-25 at 19:26 +0000, Neil Cerutti wrote:
              >Speaking of the iter builtin function, is there an example of the
              >use of the optional sentinel object somewhere I could see?
              >
              Example 1: If you use a DB-API module that doesn't support direct cursor
              iteration with "for row in cursor", you can simulate it this way:
              >
              for row in iter(cursor.fet chone, None):
              # do something
              >
              [...]
              This would, of course, be a horribly inefficient way to handle a
              database result with 1,500,000 rows. Calling fetchall() might also have
              its issues. The happy medium is to use a series of calls to fetchmany(N)
              with an appropriate value of N.

              regards
              Steve
              --
              Steve Holden +1 571 484 6266 +1 800 494 3119
              Holden Web LLC/Ltd http://www.holdenweb.com
              Skype: holdenweb http://del.icio.us/steve.holden
              --------------- Asciimercial ------------------
              Get on the web: Blog, lens and tag the Internet
              Many services currently offer free registration
              ----------- Thank You for Reading -------------

              Comment

              • Neil Cerutti

                #8
                Re: is_iterable function.

                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.

                This seems preferable to cluttering code with exception handling
                and inspecting tracebacks. But it's still basically wrong, I
                guess.

                To repost my use case:

                def deeply_mapped(f unc, iterable):
                """ Recursively apply a function to every item in a iterable object,
                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.parti al(deeply_mappe d, None)
                >>list(flattene d([[1], [2, 3, []], 4]))
                [1, 2, 3, 4]
                >>list(flattene d(((1), (2, 3, ()), 4)))
                [1, 2, 3, 4]
                >>list(flattene d([[[[]]], 1, 2, 3, 4]))
                [1, 2, 3, 4]
                >>list(flattene d([1, [[[2, 3]], 4]]))
                [1, 2, 3, 4]

                """
                for item in iterable:
                if is_iterable(ite m):
                for it in deeply_mapped(f unc, item):
                if func is None:
                yield it
                else:
                yield func(it)
                else:
                if func is None:
                yield item
                else:
                yield func(item)
                --
                Neil Cerutti

                Comment

                • Marc 'BlackJack' Rintsch

                  #9
                  Re: is_iterable function.

                  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".

                  Ciao,
                  Marc 'BlackJack' Rintsch

                  Comment

                  • Neil Cerutti

                    #10
                    Re: is_iterable function.

                    On 2007-07-26, George Sakkis <george.sakkis@ gmail.comwrote:
                    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):
                    That makes good sense.

                    Plus the subtly different way you composed is_iterable is clearer
                    than what I originally wrote. I haven't ever used a try with an
                    else.
                    if is_iterable(obj ):
                    for item in obj:
                    for flattened in flatten(item, is_iterable):
                    yield flattened
                    else:
                    yield obj
                    >
                    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.
                    I chose to implement deeply_mapped because it illustrated the
                    problem of trying to catch a TypeError exception when one might
                    be thrown by some other code. I agree with your opinion that it's
                    a design flaw, and most of my problems with the code were caused
                    by that flaw.

                    --
                    Neil Cerutti

                    Comment

                    Working...