Here I asked a question about izip_longest from itertools .
His code is:
def izip_longest_from_docs(*args, **kwds): # izip_longest('ABCD', 'xy', fillvalue='-') --> 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
An error has appeared in the documentation in the pure Python equivalent of this function. The error was that the real function was IndexError , and the aforementioned equivalent did not IndexError exceptions that were raised inside the generators sent as function parameters.
@agf solved the problem and gave a patched version of the pure Python equivalent.
But at the same time, when he wrote his decision, I made my own. And at the same time, I ran into one problem, which, I hope, will be dissolved by asking this question.
The code I came up with is this:
def izip_longest_modified_my(*args, **kwds):
There is a generator in the sentinel source code that performs a lazy evaluation. So counter() returned only when the iterator created using the chain function really needs it.
In my code, I added counter , which contains a list of a single value [0] . The reason for this was to place the mutable object in a place where all returned ret() iterators can be accessed and modified. The only place I found suitable was in function_defaults of sentinel .
If I put it inside the sentinel function, then counter will be assigned [0] each time sentinel called, and these will be different lists for all ret() s:
def sentinel(fillvalue = fillvalue): counter = [0] def ret(): counter[0] += 1 if counter[0] == len(args): raise LongestExhausted yield fillvalue return ret()
I tried setting it outside the sentinel function:
counter = 0 def sentinel(fillvalue = fillvalue): def ret(): counter += 1 if counter == len(args): raise LongestExhausted yield fillvalue return ret()
But the exception has grown: UnboundLocalError: local variable 'counter' referenced before assignment .
I added the global , but this did not help (I think because counter really not in the global scope):
counter = 0 def sentinel(fillvalue = fillvalue): global counter def ret(): counter += 1 if counter == len(args): raise LongestExhausted yield fillvalue return ret()
So my question is :
Is the approach I used (to put mutable list counter = [0] in function_defaults ) the best in this case, or is there a better way to solve this problem?