Skip to content Skip to sidebar Skip to footer

What Is The Best Way To Share A Value By All The Generators Created By A Function?

Here I asked a question about izip_longest function from itertools module. The code of it: def izip_longest_from_docs(*args, **kwds): # izip_longest('ABCD', 'xy', fillvalue='-'

Solution 1:

This has been asked many times in many forms. Read any number of other questions about mutable default arguments and the new Python 3 nonlocal keyword. On Python 2, you could use a function attribute:

defsentinel(fillvalue = fillvalue):
    defret():
        sentinel.counter += 1if sentinel.counter == len(args):
            raise LongestExhausted
        yield fillvalue
    return ret()
sentinel.counter = 0

or use global both inside ret and inside izip_longest so you're always referencing a global variable:

global counter
counter = 0defsentinel(fillvalue = fillvalue):
    defret():
        global counter
        counter += 1if counter == len(args):
            raise LongestExhausted
        yield fillvalue
    return ret()

However, using global restricts you to only one izip_longest at a time -- see the comments on the other answer.

You're also defining a new ret every time sentinel is called (once per iterator) -- you could instead do something like

global counter
counter = 0
arglen = len(args)

def ret():
    global counter
    counter += 1if counter == arglen:
        raise LongestExhausted
    return fillvalue

def sentinel():
    yieldret()

Example code for having sentinel outside izip_longest in re your question from the comments:

defsentinel(counter, arglen, fillvalue):
    defret():
        counter[0] += 1if counter[0] == arglen:
            raise LongestExhausted
        yield fillvalue
    return ret()


defizip_longest_modified_my(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')

    classLongestExhausted(Exception):
        pass

    fillers = repeat(fillvalue)
    counter = [0]
    arglen = len(args)
    iters = [chain(it, sentinel(counter, arglen, fillvalue), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except LongestExhausted:
        pass

Here you're again using a list just as a container to get around the problems accessing outer scopes in Python 2.

Solution 2:

Using a global is a bad idea, IMHO. You need to make sure to reset the counter properly between calls. But more seriously this is a generator; you don't even need threading to have multiple calls to a generator in flight at the same time, which will wreck havoc with any attempt to sanely use a global to keep track of state.

You could just explicitly pass a reference to a mutable object into sentinel, and then into ret. It looks like your code controls all the calls to them. Function parameters are the original and boring way of transferring references between scopes!

Post a Comment for "What Is The Best Way To Share A Value By All The Generators Created By A Function?"