Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python method that is also a generator function?

I'm trying to build a method that also acts like a generator function, at a flip of a switch (want_gen below).

Something like:

def optimize(x, want_gen):
    # ... declaration and validation code
    for i in range(100):
        # estimate foo, bar, baz
        # ... some code here

        x = calculate_next_x(x, foo, bar, baz)

        if want_gen:
           yield x

    if not want_gen:
       return x

But of course this doesn't work -- Python apparently doesn't allow yield and return in the same method, even though they cannot be executed simultaneously.

The code is quite involved, and refactoring the declaration and validation code doesn't make much sense (too many state variables -- I will end up with difficult-to-name helper routines of 7+ parameters, which is decidedly ugly). And of course, I'd like to avoid code duplication as much as possible.

Is there some code pattern that would make sense here to achieve the behaviour I want?


Why do I need that?

I have a rather complicated and time-consuming optimization routine, and I'd like to get feedback about its current state during runtime (to display in e.g. GUI). The old behaviour needs to be there for backwards compatibility. Multithreading and messaging is too much work for too little additional benefit, especially when cross-platform operation is necessary.

Edit: Perhaps I should have mentioned that since each optimization step is rather lengthy (there are some numerical simulations involved as well), I'd like to be able to "step in" at a certain iteration and twiddle some parameters, or abort the whole business altogether. The generators seemed like a good idea, since I could launch another iteration at my discretion, fiddling in the meantime with some parameters.

like image 952
mindcorrosive Avatar asked Nov 28 '25 09:11

mindcorrosive


2 Answers

Since all you seem to want is some sort of feedback for a long running function, why not just pass in a reference to a callback procedure that will be called at regular intervals?

like image 160
Bryan Oakley Avatar answered Nov 29 '25 22:11

Bryan Oakley


An edit to my answer, why not just always yield? You can have a function which yields a single value. If you don't want that then just choose to have your function either return a generator itself or the value:

 def stuff(x, want_gen):
     if want_gen:
         def my_gen(x):
             #code with yield
         return my_gen
     else:
         return x

That way you are always returning a value. In Python, functions are objects.

like image 34
wheaties Avatar answered Nov 30 '25 00:11

wheaties