Often I need to perform multiple assignment from a sequence(e.g. from the result of a split()) but with some semanticsthat, AFAIK, are not available in the language:Assign N first elements and silently ignore the rest (like Perl'slist assignment).>>> a, b = 'foo bar is here'.split()>>> a, b('foo', 'bar')>>>Assign the remaning items to the last variable in the variable list:>>> a, b, c = 'foo bar is here'.split()>>> a, b, c('foo', 'bar', ['is', 'here'])>>>If the sequence doesn't have enough values, set the variables to adefault value (None or any other, again similar to Perl):>>> a, b, c, d, e = 'foo bar is here'.split()>>> a, b, c, d, e('foo', 'bar', 'is', 'here', None)>>>I have designed an iterator function to help me with this, but I'mconcerned about my solution to this problem from the point of view ofpythonic adherence:Usefulness: Using an iterator for this kind of job is the rightoption, or there is something more appropriate?  Or even worse: myneed to do this kind of assignment in Python is not favoring otherlanguage features?Clearness: The implementation is solid and clear enough?  Or isthere some other way to implement this that would be cleaner?In other words, this solution is better discarded or can it beimproved?from itertools import *def iterassign(iterable, *, count, use_default=False, default=None, tail=False):    Iterattor for multiple assignment.    :param iterable: The iterable.    :param int count: The maximum count of iterations.    :param bool use_default: If set, and iterable ends before max_count,        continue iteration using :param:`default`.    :param default: Default value to use when :param:`use_default` is        set to ``True``.        it = iter(iterable)    for idx, element in enumerate(chain(islice(it, 0, count), repeat(default))):        if idx >= count: break        yield element    else:        raise StopIteration()    if tail:        yield list(it)Usage examples:>>> ret = 'foo bar is here'.split()>>> a, b = iterassign(ret, count=2)>>> a, b('foo', 'bar')>>> a, b, c = iterassign(ret, count=2, tail=True)>>> a, b, c('foo', 'bar', ['is', 'here'])>>> a, b, c, d, e = iterassign(ret, count=5, use_default=True)('foo', 'bar', 'is', 'here', None)>>>
Improving multiple assignment using an iterator function
python;python 3.x;functional programming;iterator
I'll look at each of your three examples individually.Ignoring remaining valuesIn Python 2:>>> a, b = 'foo bar is here'.split()[:2]>>> a, b('foo', 'bar')In Python 3 (_ used by convention, double underscore, __, isalso appropriate):>>> a, b, *_ = 'foo bar is here'.split()>>> a, b('foo', 'bar')When ignoring remaining values, using the built-in language features would be the most Pythonic approach.  I think they're clear enough that they do not need to be re-invented.Capture remaining valuesIn Python 2:>>> split_string = 'foo bar is here'.split()>>> a, b, c = split_string[2:] + [split_string[2:]]>>> a, b, c('is', 'here', ['is', 'here'])In Python 3:>>> a, b, *c = 'foo bar is here'.split()>>> a, b, c('is', 'here', ['is', 'here'])In Python 3, the * feature is again more Pythonic than your approach.  In Python 2, that approach won't work if you're dealing with a non-indexable type.Fill remaining values with None.  Here is a somewhat similar question.  That question and the Python 2 solution only work for lists.Python 2:>>> split_string = 'foo bar is here'.split()>>> a, b, c, d, e = split_string + [None] * (5-len(split_string))>>> a, b, c, d, e('foo', 'bar', 'is', 'here', None)Python 3.5 (this is not very clear in my opinion):>>> a, b, c, d, e, *_ = *'foo bar is here'.split(), *[None]*5>>> a, b, c, d, e('foo', 'bar', 'is', 'here', None)There isn't really a built-in language construct for this so this case might warrant re-inventing.  However, this kind of None-filling seems rare and may not necessarily be clear to other Python programmers.A review of your actual implementationDon't use import * in Python.  In Python we value our namespaces highly because namespaces allow us to see what variables came from what modules.from itertools import *Do this instead:from itertools import chain, islice, repeatYou didn't use use_default in your code at all.  It seems like you could use another islice instead of an enumerate to restructure your code like this:def iterassign(iterable, *, count, default=None, tail=False):    it = iter(iterable)    for x in islice(chain(islice(it, 0, count), repeat(default)), 0, count):        yield x    if tail:        yield list(it)Iterators are the right solution to this problem (assuming we've decided it's one that needs solving).If we want to rewrite this to only handle case 3 (because we've already decided we can just use Python's built-in language features for case 1 and 2, we could rewrite like this:def fill_remaining(iterable, *, length, default=None):    Return iterable padded to at least ``length`` long.    return islice(chain(iterable, repeat(default)), 0, length)Which would work like this:>>> a, b, c, d, e = fill_remaining(ret, length=5)>>> a, b, c, d, e('foo', 'bar', 'is', 'here', None)