Search code examples
pythonoopgenerator

Define a generator which updates global variables before the first __next__() call


Given the following function in python I would like to update the global variable before calling "next()". Let me show it to you with an example.

# some script happening before
 
# a (global) variable is created
L = 0

def generate(batch=128, ID=0, YIND=0):

  # just a call to a global dictionary
  x_, y_ = global_dict[id_to_keys[ID]]

  # update global variable please
  global L
  L = len(x_)

  X,Y=[],[]
  counter=0

  for i,x in enumerate(x_):

    counter+=1
    X.append(x)
    Y.append(y_[i])

    if counter==batch: 

      counter=0
      yield np.asarray(X[-batch:]), np.asarray(Y[-batch:])

Then you can run:

print(L)
g = generate()
print(f'I would like this to output some number but it outputs ',L)
_ = g.__next__()
print(f'I would like this to output some number and it outputs ',L)

Which outputs the following:

I would like this to output some number but it outputs 0
I would like this to output some number and it outputs 12312

Finally: please note that there is a way of doing this via a class definition that has a class variable, but I'm currently wondering for a full "functional" implementation.

Thank you for your time


Solution

  • I’m not entirely sure if I understand correctly what you are trying to do. But the thing is that a generator function in Python only starts to execute once the generator is being enumerated:

    def gen():
        print('before 1')
        yield 1
        print('before 2')
        yield 2
        print('after 2')
    
    >>> g = gen()
    >>> next(g)
    before 1
    1
    >>> next(g)
    before 2
    2
    >>> next(g)
    after 2
    Traceback (most recent call last):
      File "<pyshell#9>", line 1, in <module>
        next(g)
    StopIteration
    

    So if you want to run code before the generator is being enumerated, you cannot have that code be part of the generator function itself.

    What you can do instead is to have a normal function that does something and then returns a created generator:

    def gen_internal():
        yield 1
        print('before 2')
        yield 2
        print('after 2')
    
    def gen():
        print('before 1')
        return gen_internal()
    
    >>> g = gen()
    before 1
    >>> next(g)
    1
    >>> next(g)
    before 2
    2
    >>> next(g)
    after 2
    

    Since you apparently only set L once, before the first yield, this might be what you want to do.