What is the best way to share a value by all the generators created by a function?
<p><a href="http://stackoverflow.com/questions/7392902/izip-longest-in-itertools-how-does-rasing-indexerror-inside-the-iterator-work">Here</a> I asked a question about <a href="http://docs.python.org/library/itertools.html#itertools.izip_longest" rel="nofollow"><code>izip_longest</code></a> function from <code>itertools</code> module.</p>

<p>The code of it:</p>

<pre><code>def izip_longest_from_docs(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --&gt; Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
        yield counter()         # yields the fillvalue, or raises IndexError
    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except IndexError:
        pass
</code></pre>

<p>There appeared to be an error in the documentation in the pure Python equivalent of that function. The error was that the real function did and the abovementioned equivalent didn't 
propagate <code>IndexError</code> exceptions that were raised inside the generators sent as the function parameters.</p>

<p>@agf solved the problem and <a href="http://stackoverflow.com/questions/7392902/izip-longest-in-itertools-how-does-rasing-indexerror-inside-the-iterator-work/7393480#7393480">gave</a> a corrected version of the pure Python equivalent.</p>

<p>But at the same time when he was writing his solution I made my own. And while making it I faced one problem which I hope will be unraveled by asking this question.</p>

<p>The code that I came up with is this:</p>

<pre><code>def izip_longest_modified_my(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --&gt; Ax By C- D-
    fillvalue = kwds.get('fillvalue')

    class LongestExhausted(Exception):
        pass

    def sentinel(fillvalue = fillvalue, counter = [0]):
        def ret():
            counter[0] += 1
            if counter[0] == len(args):
                raise LongestExhausted
            yield fillvalue
        return ret()

    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except LongestExhausted:
        pass 
</code></pre>

<p>In the original code <code>sentinel</code> is a generator which implements lazy evaluation. So that <code>counter()</code> is returned only when it's actually needed by the iterator created using <code>chain</code> function.</p>

<p>In my code I added a <code>counter</code> which holds a list of one value <code>[0]</code>. The reason for that was to put a <code>mutable</code> object into some place where it can be accessed by all the returned iterators <code>ret()</code> and changed by them. The only place I found suitable was in the <code>function_defaults</code> of <code>sentinel</code>. </p>

<p>If I put it inside the <code>sentinel</code> function, then the <code>counter</code> would be assigned to <code>[0]</code> on every call of <code>sentinel</code> and that would be different lists for all the <code>ret()</code>s:</p>

<pre><code>def sentinel(fillvalue = fillvalue):
    counter = [0]
    def ret():
        counter[0] += 1
        if counter[0] == len(args):
            raise LongestExhausted
        yield fillvalue
    return ret()
</code></pre>

<p>I tried to put it outside of the <code>sentinel</code> function:</p>

<pre><code>counter = 0
def sentinel(fillvalue = fillvalue):
    def ret():
        counter += 1
        if counter == len(args):
            raise LongestExhausted
        yield fillvalue
    return ret()
</code></pre>

<p>But the exception rose: <code>UnboundLocalError: local variable 'counter' referenced before assignment</code>.</p>

<p>I added <code>global</code> keyword, but it didn't help (I think because <code>counter</code> is really not in the <code>global</code> scope):</p>

<pre><code>counter = 0
def sentinel(fillvalue = fillvalue):
    global counter
    def ret():
        counter += 1
        if counter == len(args):
            raise LongestExhausted
        yield fillvalue
    return ret()
</code></pre>

<p><strong>So, my question is</strong>:</p>

<p>Is the approach that I used (to put <code>mutable</code> list <code>counter = [0]</code> to <code>function_defaults</code>) the best in this case, or there is some better way to solve this problem?</p>
