Test for structure

Martin Miller ggrp1.20.martineau at dfgh.net
Mon Feb 21 14:00:15 EST 2005


Testing for the '__iter__' (or even '__getitem__') attribute doesn't
really address the problem, nor does trying to execute the statement
'itr = iter(a)'.

To use EAPF and answer the OP's original question, which was

> So how can I test if a variable 'a' is either a single character
> string or a list?

I think the best answer would be to use Robin Munn's suggestion (see
http://groups-beta.google.com/group/comp.lang.python/msg/c8befd4bed517bbc)
as mentioned in the link in Simon Brunning's post) namely:

    try:
        a + ''
    except TypeError:
        pass
    else:
        a = [a]

However, to handle the more general problem of allow *any* argument to
be either a single item or a list seems to require a combination of
both EAPF and LBYL. This is the best solution I've been able to come up
with so far:

def asList(arg):
    """Makes sure the argument it is passed is a Python list.
    If it is, it is just returned, otherwise a (possibly empty)
    list is created and returned with the single item in it.

    asList() can used to create flexible interfaces which allow
    arguments to be passed to them that are either single items or
    lists of items. By applying this function before using the
    values in arguments, single and multi-valued cases can be
    handled by general list-handling code in the function or
    method.

    As a special case, a single argument with the value None is
    converted into an empty list (instead of converted into the
    list [None]).

    asList(arg) ==> list
    """

    if arg is None:
        return []
    elif isinstance(arg, basestring): # special case strings (to
                                      # avoid list(<string>))
        return [arg]
    else:
        try:
            return list(arg)
        except TypeError:
            return [arg]


if __name__ == "__main__":

    def example(items=None):
        """Sample function that can be called with a single argument
        that can be a single or list of items.
        """
        itemList = asList(items)
        if not itemList:
            print "example() called with empty list or None argument"
        else:
            print "example() called with argument containing %d " \
                  "thing%s" % \
                  (len(itemList), ('','s')[len(itemList)>1])
        for i, item in enumerate(itemList):
            print "  items[%d] = %s" % (i, repr(item))

    example(42)
    example((1,2,3))
    example([4,5,6,7])
    example('abc')
    example(u'def')
    example(["aaa", 111, (4,5), 2.01])
    example(None) #  Note that this will become an empty list
    example()     #  same in this case

Which produces the following output:

example() called with argument containing 1 thing
  items[0] = 42
example() called with argument containing 3 things
  items[0] = 1
  items[1] = 2
  items[2] = 3
example() called with argument containing 4 things
  items[0] = 4
  items[1] = 5
  items[2] = 6
  items[3] = 7
example() called with argument containing 1 thing
  items[0] = 'abc'
example() called with argument containing 1 thing
  items[0] = u'def'
example() called with argument containing 4 things
  items[0] = 'aaa'
  items[1] = 111
  items[2] = (4, 5)
  items[3] = 2.0099999999999998
example() called with empty list or None argument
example() called with empty list or None argument

Can this be improved or is there anything wrong or overly limiting
about it?

TIA,
Martin


=====================
Steven Bethard wrote:
> Terry Hancock wrote:
>  > But you probably shouldn't do that. You should probably just test
to
>  > see if the object is iterable --- does it have an __iter__ method?
>  >
>  > Which might look like this:
>  >
>  > if hasattr(a, '__iter__'):
>  >     print "'a' quacks like a duck"
>
> Martin Miller top-posted:
> > I don't believe you can use the test for a __iter__ attribute in
this
> > case, for the following reason:
> >
> >>>>c1 = 'abc'
> >>>>c2 = ['de', 'fgh', 'ijkl']
> >>>>hasattr(c1, '__iter__')
> > False
> >
> >>>>hasattr(c2, '__iter__')
> > True
>
> Right.  str and unicode objects support iteration through the old
> __getitem__ protocol, not the __iter__ protocol.  If you want to use
> something as an iterable, just use it and catch the exception:
>
> try:
>      itr = iter(a)
> except TypeError:
>      # 'a' is not iterable
> else:
>      # 'a' is iterable
>
> Another lesson in why EAPF is often better than LBYL in Python[1].
> 
> STeVe
> 
> [1] http://www.python.org/moin/PythonGlossary




More information about the Python-list mailing list