default value in a list

T

TB

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:
if line could have less than three fields?

Thanks,
TB
 
S

Steve Holden

TB said:
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:



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
regards
Steve
 
L

Larry Bates

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
 
P

Paul McGuire

TB said:
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:

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
 
S

Steven Bethard

Paul said:
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
 
J

Jeff Shannon

TB said:
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:



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

Fredrik Lundh

Paul said:
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>
 
A

Alex Martelli

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

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
 
P

Peter Otten

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
 
P

Peter Otten

Peter said:
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:
.... if len(items) >= len(defaults):
.... return items[:len(defaults)]
.... return items + defaults[len(items):]
....(1, 2)

Peter
 
N

Nick Craig-Wood

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

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

Alex Martelli

Nick Craig-Wood said:
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
 
B

Bengt Richter

TB said:
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:



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.

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

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:
... return a, b, c, d, e
... ('a', 'b', 'c', None, None)

But then, might as well do:
... if nreq <= len(args): return args[:nreq]
... return args+ (nreq-len(args))*(None,)
... ('a', 'b', 'c')

But usually, I would like n + tail, where I know n is a safe bet, e.g.,
... return args[:n]+(args[n:],)
... ('a', 'b', ())


People have asked to be able to spell that as

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



Regards,
Bengt Richter
 
N

Nick Craig-Wood

Alex Martelli said:
Nick Craig-Wood said:
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!
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!
 
M

Michael Spencer

Alex Martelli wrote:
[explanation and the following code:]
... it.chain(
... line.split(':'),
... it.repeat(some_default),
... ),
... 3)
...
...
... 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?
... return it.islice(it.chain(iterable, it.repeat(default)), N)
...('A', 'B', 'C', None)

Michael
 
R

Reinhold Birkenfeld

Michael said:
Alex Martelli wrote:
[explanation and the following code:]
... it.chain(
... line.split(':'),
... it.repeat(some_default),
... ),
... 3)
...
...
... 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?
... return it.islice(it.chain(iterable, it.repeat(default)), N)
...('A', 'B', 'C', None)

Good idea!

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

Reinhold
 
N

Nick Coghlan

Reinhold said:
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. . .
 
R

Reinhold Birkenfeld

Nick said:
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 ;)
 
T

TB

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top