default value in a list

Discussion in 'Python' started by TB, Jan 21, 2005.

  1. TB

    TB Guest

    Hi,

    Is there an elegant way to assign to a list from a list of unknown
    size? For example, how could you do something like:

    >>> a, b, c = (line.split(':'))

    if line could have less than three fields?

    Thanks,
    TB
    TB, Jan 21, 2005
    #1
    1. Advertising

  2. TB

    Steve Holden Guest

    TB wrote:

    > Hi,
    >
    > Is there an elegant way to assign to a list from a list of unknown
    > size? For example, how could you do something like:
    >
    >
    >>>> a, b, c = (line.split(':'))

    >
    > if line could have less than three fields?
    >

    l = line.split(':')

    l is a list, whose length will be one more than the number of colons in
    the line.

    You can access the elements of the list using a[0], a[2], and so on. For
    example:

    >>> line = "This:is:a:sample:line"
    >>> l = line.split(':')
    >>> l

    ['This', 'is', 'a', 'sample', 'line']
    >>> for w in l:

    ... print w
    ...
    This
    is
    a
    sample
    line
    >>> len(l)

    5
    >>>


    regards
    Steve
    --
    Steve Holden http://www.holdenweb.com/
    Python Web Programming http://pydish.holdenweb.com/
    Holden Web LLC +1 703 861 4237 +1 800 494 3119
    Steve Holden, Jan 21, 2005
    #2
    1. Advertising

  3. TB

    Larry Bates Guest

    What do you want put into the "missing" variables?
    I'll assume None. Something like following
    works:

    values=line.split(':')
    try: a=values.pop(0)
    except IndexError: a=None
    try: b=values.pop(0)
    except IndexError: b=None
    try: c=values.pop(0)
    except IndexError: c=None


    Larry Bates



    TB wrote:
    > Hi,
    >
    > Is there an elegant way to assign to a list from a list of unknown
    > size? For example, how could you do something like:
    >
    >
    >>>> a, b, c = (line.split(':'))

    >
    > if line could have less than three fields?
    >
    > Thanks,
    > TB
    >
    Larry Bates, Jan 21, 2005
    #3
  4. TB

    Paul McGuire Guest

    "TB" <> wrote in message
    news:...
    > Hi,
    >
    > Is there an elegant way to assign to a list from a list of unknown
    > size? For example, how could you do something like:
    >
    > >>> a, b, c = (line.split(':'))

    > if line could have less than three fields?
    >
    > Thanks,
    > TB
    >

    I asked a very similar question a few weeks ago, and from the various
    suggestions, I came up with this:

    line = "AAAA:BBB"
    expand = lambda lst,default,minlen : (lst + [default]*minlen)[0:minlen]
    a,b,c = expand( line.split(":"), "", 3 )
    print a
    print b
    print c

    -- Paul
    Paul McGuire, Jan 21, 2005
    #4
  5. Paul McGuire wrote:
    > expand = lambda lst,default,minlen : (lst + [default]*minlen)[0:minlen]


    Or if you're afraid of lambda like me:

    def expand(lst,default,minlen):return (lst + [default]*minlen)[0:minlen]

    or perhaps more readably:

    def expand(lst, default, minlen):
    return (lst + [default]*minlen)[0:minlen]

    No need for an anonymous function when you're naming it. ;)

    Steve
    Steven Bethard, Jan 22, 2005
    #5
  6. TB

    Jeff Shannon Guest

    TB wrote:

    > Hi,
    >
    > Is there an elegant way to assign to a list from a list of unknown
    > size? For example, how could you do something like:
    >
    >
    >>>> a, b, c = (line.split(':'))

    >
    > if line could have less than three fields?


    (Note that you're actually assigning to a group of local variables,
    via tuple unpacking, not assigning to a list...)

    One could also do something like this:

    >>> l = "a:b:c".split(':')
    >>> a, b, c, d, e = l + ([None] * (5 - len(l)))
    >>> print (a, b, c, d, e)

    ('a', 'b', 'c', None, None)
    >>>


    Personally, though, I can't help but think that, if you're not certain
    how many fields are in a string, then splitting it into independent
    variables (rather than, say, a list or dict) *cannot* be an elegant
    solution. If the fields deserve independent names, then they must
    have a definite (and distinct) meaning; if they have a distinct
    meaning (as opposed to being a series of similar items, in which case
    you should keep them in a list), then which field is it that's
    missing? Are you sure it's *always* the last fields? This feels to
    me like the wrong solution to any problem.

    Hm, speaking of fields makes me think of classes.

    >>> class LineObj:

    .... def __init__(self, a=None, b=None, c=None, d=None, e=None):
    .... self.a = a
    .... self.b = b
    .... self.c = c
    .... self.d = d
    .... self.e = e
    ....
    >>> l = "a:b:c".split(':')
    >>> o = LineObj(*l)
    >>> o.__dict__

    {'a': 'a', 'c': 'c', 'b': 'b', 'e': None, 'd': None}
    >>>


    This is a bit more likely to be meaningful, in that there's almost
    certainly some logical connection between the fields of the line
    you're splitting and keeping them as a class demonstrates that
    connection, but it still seems a bit smelly to me.

    Jeff Shannon
    Technician/Programmer
    Credit International
    Jeff Shannon, Jan 22, 2005
    #6
  7. Paul McGuire wrote:

    > I asked a very similar question a few weeks ago, and from the various
    > suggestions, I came up with this:
    >
    > expand = lambda lst,default,minlen : (lst + [default]*minlen)[0:minlen]


    I wouldn't trust whoever suggested that. if you want a function, use a function:

    def expand(seq, default, minlen):
    return (seq + [default]*minlen)[:minlen]

    </F>
    Fredrik Lundh, Jan 22, 2005
    #7
  8. TB <> wrote:

    > Is there an elegant way to assign to a list from a list of unknown
    > size? For example, how could you do something like:
    >
    > >>> a, b, c = (line.split(':'))

    > if line could have less than three fields?


    import itertools as it

    a, b, c = it.islice(
    it.chain(
    line.split(':'),
    it.repeat(some_default),
    ),
    3)

    I find itertools-based solutions to be generally quite elegant.

    This one assumes you want to assign some_default to variables in the LHS
    target beyond the length of the RHS list. If what you want is to repeat
    the RHS list over and over instead, this simplifies the first argument
    of islice:

    a, b, c = it.islice(it.cycle(line.split(':')), 3)

    Of course, you can always choose to write your own generator instead of
    building it up with itertools. itertools solutions tend to be faster,
    and I think it's good to get familiar with that precious modules, but
    without such familiarity many readers may find a specially coded
    generator easier to follow. E.g.:

    def pad_with_default(N, iterable, default=None):
    it = iter(iterable)
    for x in it:
    if N<=0: break
    yield x
    N -= 1
    while N>0:
    yield default
    N -= 1

    a, b, c = pad_with_default(3, line.split(':'))


    The itertools-based solution hinges on a higher level of abstraction,
    glueing and tweaking iterators as "atoms"; the innards of a custom coded
    generator tend to be programmed at a lower level of abstraction,
    reasoning in item-by-item mode. There are pluses and minuses to each
    approach; I think in the long range higher abstraction pays off, so it's
    worth the investment to train yourself to use itertools.


    In the Python Cookbook new 2nd edition, due out in a couple months,
    we've added a whole new chapter about iterators and generators, since
    it's such a major subfield in today's Python (as evidenced by the wealth
    of recipes submitted to Activestate's online cookbook sites on the
    subject). A couple of recipes have do with multiple unpacking
    assignment -- one of them, in particular, is an evil hack which peers
    into the caller's bytecode to find out how many items are on the LHS, so
    you don't have to pass that '3' explicitly. I guess that might be
    considered "elegant", for suitably contorted meanings of "elegant"...
    it's on the site, too, but I don't have the URL at hand. It's
    instructive, anyway, but I wouldn't suggest actually using it in any
    kind of production code...


    Alex
    Alex Martelli, Jan 22, 2005
    #8
  9. TB

    Peter Otten Guest

    Paul McGuire wrote:

    >> Is there an elegant way to assign to a list from a list of unknown
    >> size? For example, how could you do something like:
    >>
    >> >>> a, b, c = (line.split(':'))

    >> if line could have less than three fields?


    > I asked a very similar question a few weeks ago, and from the various
    > suggestions, I came up with this:
    >
    > line = "AAAA:BBB"
    > expand = lambda lst,default,minlen : (lst + [default]*minlen)[0:minlen]
    > a,b,c = expand( line.split(":"), "", 3 )


    Here is an expand() variant that is not restricted to lists but works with
    arbitrary iterables:

    from itertools import chain, repeat, islice

    def expand(iterable, length, default=None):
    return islice(chain(iterable, repeat(default)), length)

    Peter
    Peter Otten, Jan 22, 2005
    #9
  10. TB

    Peter Otten Guest

    Peter Otten wrote:

    > Paul McGuire wrote:
    >
    >>> Is there an elegant way to assign to a list from a list of unknown
    >>> size? For example, how could you do something like:
    >>>
    >>> >>> a, b, c = (line.split(':'))
    >>> if line could have less than three fields?

    >
    >> I asked a very similar question a few weeks ago, and from the various
    >> suggestions, I came up with this:
    >>
    >> line = "AAAA:BBB"
    >> expand = lambda lst,default,minlen : (lst + [default]*minlen)[0:minlen]
    >> a,b,c = expand( line.split(":"), "", 3 )

    >
    > Here is an expand() variant that is not restricted to lists but works with
    > arbitrary iterables:
    >
    > from itertools import chain, repeat, islice
    >
    > def expand(iterable, length, default=None):
    > return islice(chain(iterable, repeat(default)), length)


    Also nice, IMHO, is allowing individual defaults for different positions in
    the tuple:

    >>> def expand(items, defaults):

    .... if len(items) >= len(defaults):
    .... return items[:len(defaults)]
    .... return items + defaults[len(items):]
    ....
    >>> expand((1, 2, 3), (10, 20, 30, 40))

    (1, 2, 3, 40)
    >>> expand((1, 2, 3), (10, 20))

    (1, 2)

    Peter
    Peter Otten, Jan 22, 2005
    #10
  11. TB <> wrote:
    > Is there an elegant way to assign to a list from a list of unknown
    > size? For example, how could you do something like:
    >
    > >>> a, b, c = (line.split(':'))

    > if line could have less than three fields?


    You could use this old trick...

    a, b, c = (line+"::").split(':')[:3]

    Or this version if you want something other than "" as the default

    a, b, b = (line.split(':') + 3*[None])[:3]

    BTW This is a feature I miss from perl...

    --
    Nick Craig-Wood <> -- http://www.craig-wood.com/nick
    Nick Craig-Wood, Jan 22, 2005
    #11
  12. Nick Craig-Wood <> wrote:
    ...
    > Or this version if you want something other than "" as the default
    >
    > a, b, b = (line.split(':') + 3*[None])[:3]


    Either you mean a, b, c -- or you're being subtler than I'm grasping.


    > BTW This is a feature I miss from perl...


    Hmmm, I understand missing the ``and all the rest goes here'' feature
    (I'd really love it if the rejected
    a, b, *c = whatever
    suggestion had gone through, ah well), but I'm not sure what exactly
    you'd like to borrow instead -- blissfully by now I've forgotten a lot
    of the perl I used to know... care to clarify?


    Alex
    Alex Martelli, Jan 22, 2005
    #12
  13. On Fri, 21 Jan 2005 17:04:11 -0800, Jeff Shannon <> wrote:

    >TB wrote:
    >
    >> Hi,
    >>
    >> Is there an elegant way to assign to a list from a list of unknown
    >> size? For example, how could you do something like:
    >>
    >>
    >>>>> a, b, c = (line.split(':'))

    >>
    >> if line could have less than three fields?

    >
    >(Note that you're actually assigning to a group of local variables,
    >via tuple unpacking, not assigning to a list...)
    >
    >One could also do something like this:
    >
    > >>> l = "a:b:c".split(':')
    > >>> a, b, c, d, e = l + ([None] * (5 - len(l)))
    > >>> print (a, b, c, d, e)

    >('a', 'b', 'c', None, None)
    > >>>


    Or
    >>> a, b, c, d, e = ('a:b:c'.split(':')+[None]*4)[:5]
    >>> print (a, b, c, d, e)

    ('a', 'b', 'c', None, None)

    You could even be profligate and use *5 in place of that *4,
    if that makes an easier idiom ;-)

    Works if there's too many too:

    >>> a, b = ('a:b:c'.split(':')+[None]*2)[:2]
    >>> print (a, b)

    ('a', 'b')



    >
    >Personally, though, I can't help but think that, if you're not certain
    >how many fields are in a string, then splitting it into independent
    >variables (rather than, say, a list or dict) *cannot* be an elegant
    >solution. If the fields deserve independent names, then they must
    >have a definite (and distinct) meaning; if they have a distinct
    >meaning (as opposed to being a series of similar items, in which case
    >you should keep them in a list), then which field is it that's
    >missing? Are you sure it's *always* the last fields? This feels to
    >me like the wrong solution to any problem.
    >
    >Hm, speaking of fields makes me think of classes.
    >
    > >>> class LineObj:

    >... def __init__(self, a=None, b=None, c=None, d=None, e=None):
    >... self.a = a
    >... self.b = b
    >... self.c = c
    >... self.d = d
    >... self.e = e
    >...
    > >>> l = "a:b:c".split(':')
    > >>> o = LineObj(*l)
    > >>> o.__dict__

    >{'a': 'a', 'c': 'c', 'b': 'b', 'e': None, 'd': None}
    > >>>

    >
    >This is a bit more likely to be meaningful, in that there's almost
    >certainly some logical connection between the fields of the line
    >you're splitting and keeping them as a class demonstrates that
    >connection, but it still seems a bit smelly to me.
    >

    That gives me an idea:

    >>> def foo(a=None, b=None, c=None, d=None, e=None, *ignore):

    ... return a, b, c, d, e
    ...
    >>> a, b, c, d, e = foo(*'a:b:c'.split(':'))
    >>> print (a, b, c, d, e)

    ('a', 'b', 'c', None, None)

    But then, might as well do:

    >>> def bar(nreq, *args):

    ... if nreq <= len(args): return args[:nreq]
    ... return args+ (nreq-len(args))*(None,)
    ...
    >>> a, b, c, d, e = bar(5, *'a:b:c'.split(':'))
    >>> print (a, b, c, d, e)

    ('a', 'b', 'c', None, None)
    >>> a, b = bar(2, *'a:b:c'.split(':'))
    >>> print (a, b)

    ('a', 'b')
    >>> a, b, c = bar(3, *'a:b:c'.split(':'))
    >>> print (a, b, c)

    ('a', 'b', 'c')

    But usually, I would like n + tail, where I know n is a safe bet, e.g.,

    >>> def ntail(n, *args):

    ... return args[:n]+(args[n:],)
    ...
    >>> a, b, t = ntail(2, *'a:b:c:d:e'.split(':'))
    >>> print (a, b, t)

    ('a', 'b', ('c', 'd', 'e'))
    >>> a, b, t = ntail(2, *'a:b'.split(':'))
    >>> print (a, b, t)

    ('a', 'b', ())


    People have asked to be able to spell that as

    a, b, *t = 'a:b:c:d:e'.split(':')



    Regards,
    Bengt Richter
    Bengt Richter, Jan 22, 2005
    #13
  14. Alex Martelli <> wrote:
    > Nick Craig-Wood <> wrote:
    > ...
    > > Or this version if you want something other than "" as the default
    > >
    > > a, b, b = (line.split(':') + 3*[None])[:3]

    >
    > Either you mean a, b, c -- or you're being subtler than I'm
    > grasping.


    Just a typo - I meant c!

    > > BTW This is a feature I miss from perl...

    >
    > Hmmm, I understand missing the ``and all the rest goes here'' feature
    > (I'd really love it if the rejected
    > a, b, *c = whatever
    > suggestion had gone through, ah well), but I'm not sure what exactly
    > you'd like to borrow instead -- blissfully by now I've forgotten a lot
    > of the perl I used to know... care to clarify?


    I presume your construct above is equivalent to

    my ($a, $b, @c) = split /.../;

    which I do indeed miss.

    Sometimes I miss the fact that in the below any unused items are set
    to undef, rather than an exception being raised

    my ($a, $b, $c) = @array;

    However, I do appreciate the fact (for code reliability) that the
    python equivalent

    a, b, c = array

    will blow up if there aren't exactly 3 elements in array.

    So since I obviously can't have my cake an eat it here, I'd leave
    python how it is for the second case, and put one of the suggestions
    in this thread into my toolbox / the standard library.

    BTW I've converted a lot of perl programs to python so I've come
    across a lot of little things like this!

    --
    Nick Craig-Wood <> -- http://www.craig-wood.com/nick
    Nick Craig-Wood, Jan 22, 2005
    #14
  15. Alex Martelli wrote:
    [explanation and the following code:]

    > >>> a, b, c = it.islice(

    > ... it.chain(
    > ... line.split(':'),
    > ... it.repeat(some_default),
    > ... ),
    > ... 3)
    > ...
    > ...
    > >>> def pad_with_default(N, iterable, default=None):

    > ... it = iter(iterable)
    > ... for x in it:
    > ... if N<=0: break
    > ... yield x
    > ... N -= 1
    > ... while N>0:
    > ... yield default
    > ... N -= 1


    Why not put these together and put it in itertools, since the requirement seems
    to crop up every other week?

    >>> line = "A:B:C".split(":")

    ...
    >>> def ipad(N,iterable, default = None):

    ... return it.islice(it.chain(iterable, it.repeat(default)), N)
    ...
    >>> a,b,c,d = ipad(4,line)
    >>> a,b,c,d

    ('A', 'B', 'C', None)

    Michael
    Michael Spencer, Jan 22, 2005
    #15
  16. Michael Spencer wrote:
    > Alex Martelli wrote:
    > [explanation and the following code:]
    >
    >> >>> a, b, c = it.islice(

    >> ... it.chain(
    >> ... line.split(':'),
    >> ... it.repeat(some_default),
    >> ... ),
    >> ... 3)
    >> ...
    >> ...
    >> >>> def pad_with_default(N, iterable, default=None):

    >> ... it = iter(iterable)
    >> ... for x in it:
    >> ... if N<=0: break
    >> ... yield x
    >> ... N -= 1
    >> ... while N>0:
    >> ... yield default
    >> ... N -= 1

    >
    > Why not put these together and put it in itertools, since the requirement seems
    > to crop up every other week?
    >
    > >>> line = "A:B:C".split(":")

    > ...
    > >>> def ipad(N,iterable, default = None):

    > ... return it.islice(it.chain(iterable, it.repeat(default)), N)
    > ...
    > >>> a,b,c,d = ipad(4,line)
    > >>> a,b,c,d

    > ('A', 'B', 'C', None)


    Good idea!

    (+1 if this was posted on python-dev!)

    Reinhold
    Reinhold Birkenfeld, Jan 22, 2005
    #16
  17. TB

    Nick Coghlan Guest

    Reinhold Birkenfeld wrote:
    >>Why not put these together and put it in itertools, since the requirement seems
    >>to crop up every other week?
    >>
    >> >>> line = "A:B:C".split(":")

    >> ...
    >> >>> def ipad(N,iterable, default = None):

    >> ... return it.islice(it.chain(iterable, it.repeat(default)), N)
    >> ...
    >> >>> a,b,c,d = ipad(4,line)
    >> >>> a,b,c,d

    >>('A', 'B', 'C', None)

    >
    >
    > Good idea!
    >
    > (+1 if this was posted on python-dev!)


    Please, please Google the python-dev archives before doing so ;)

    Cheers,
    Nick.
    I seem to recall 'tuple unpacking' as the appropriate phrase. . .

    --
    Nick Coghlan | | Brisbane, Australia
    ---------------------------------------------------------------
    http://boredomandlaziness.skystorm.net
    Nick Coghlan, Jan 23, 2005
    #17
  18. Nick Coghlan wrote:
    > Reinhold Birkenfeld wrote:
    >>>Why not put these together and put it in itertools, since the requirement seems
    >>>to crop up every other week?
    >>>
    >>> >>> line = "A:B:C".split(":")
    >>> ...
    >>> >>> def ipad(N,iterable, default = None):
    >>> ... return it.islice(it.chain(iterable, it.repeat(default)), N)
    >>> ...
    >>> >>> a,b,c,d = ipad(4,line)
    >>> >>> a,b,c,d
    >>>('A', 'B', 'C', None)

    >>
    >>
    >> Good idea!
    >>
    >> (+1 if this was posted on python-dev!)

    >
    > Please, please Google the python-dev archives before doing so ;)


    I have no intent of doing so, I would leave that to more experienced
    people...

    Reinhold ;)
    Reinhold Birkenfeld, Jan 23, 2005
    #18
  19. TB

    TB Guest

    Thanks very much for all the responses. They were useful to me and
    I'll probably refer back to them again in the future.

    TB
    TB, Jan 25, 2005
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael Ahlers
    Replies:
    1
    Views:
    5,638
    Martin Honnen
    Jul 12, 2004
  2. Bo Peng

    default value for list access?

    Bo Peng, Feb 27, 2005, in forum: Python
    Replies:
    4
    Views:
    303
    Bo Peng
    Feb 27, 2005
  3. C Gillespie
    Replies:
    3
    Views:
    391
    Peter Hansen
    Mar 22, 2005
  4. LaundroMat
    Replies:
    50
    Views:
    938
    Hendrik van Rooyen
    Oct 14, 2006
  5. Replies:
    6
    Views:
    353
    Rolf Magnus
    Jun 22, 2005
Loading...

Share This Page