2

I have a function with a while loop. Before I start that function I want to be able to decide whether I want to export an image every time I loop through it or if I only want to show it, do both or do neither.

I don't want to but an "if" into the loop that is asked every time when I go through it so I thought how about I just write 4 different versions of that function (loop, loop_show, loop_export, loop_show_export). But I thought there must be a way to not copy paste the rest of the function.

Is there a way to do that ? Or is there an even better way with Decorators or something else ?

def main():
    ...
    while True:
        ...
        ####### code that I need to dis-/enable ####### 
        for image in images:
            video_in.write(image) # preparation for export
            Show.show_win('in', image) # show input
        ###############################################
        ...
le_lemon
  • 107
  • 1
  • 11
  • 1
    Have your function take an argument to switch between the several options. If you show us this function ([edit](https://stackoverflow.com/posts/58321645/edit) your post), we can provide better help – Valentino Oct 10 '19 at 11:19
  • 1
    Since checking against a boolean is an operation that takes nearly no time, everything but the `if` is – baao Oct 10 '19 at 11:20
  • 2
    put an `if` in the loop, don't try to outsmart yourself. copy/pasting 4 similar functions is definitely not the way to go. – lenik Oct 10 '19 at 11:20
  • @Valentino if have added the important snippet of the function for you – le_lemon Oct 10 '19 at 11:29

5 Answers5

3

If I understood correctly, this is what you need:

def main(show=False, export=False):
    ...
    while True:
        ...
        ####### code that I need to dis-/enable ####### 
        for image in images:
            if export:
                video_in.write(image) # preparation for export
            if show:
                Show.show_win('in', image) # show input
        ###############################################
        ...

When calling your function as main() the show and export argument are by default False, so the two if in the loop are not performed.
If you call main(show=True) the if show: is performed.
If you call main(export=True) the if export: is performed.
If you call main(show=True, export=True) both if are performed.

As people said in the comment, checking a boolean takes nearly no time, the code is readable and there are not duplicated lines in nearly equal functions. No need to look for more elaborated solutions.

Valentino
  • 7,291
  • 6
  • 18
  • 34
2

That's too cool. I understand very well what you mean. I just went through that lately. The if will be useless after the check is done and you switch to the other branch, that it will keep running.

For instance here an example to explain the problem. Imaging we have a method or callback that need to run something only the first time. Or after some number of iteration. Once it's switch it will keep running only on the else or the second code.

So having a useless if is a useless overhead. If we can switch completely the method it will be nice.

Here how it can be done in JavaScript

const execCallback = version1WithCheck;

function version1WithCheck() {
    if (myCondition) {
        // do something
    } else {
        execCallback = version2NoCheck; //<----- changing the ref of your execCallback variable (which need to be global (in js it will be a closure))
        execCallback();
    }
}

function version2NoCheck() {

}


function someEventListener() {
    execCallback();
};

event.listen(someEventListener);

Here a real example:

    private _tradesInsertToDbWithCheck(trades: Trade[]) {
        if (!this._checkIfTradesNeedToBeInserted(trades)) {
            const sortedTradesToBeInserted = trades.filter((trade: Trade) => {
                const firstTradeInfo = this._firstTradeMapping[
                    objectToExchangeSymbolSignature<Trade>(trade)
                ];

                return trade.id >= firstTradeInfo.id;
            });

            this._insertTradesIntoDb(sortedTradesToBeInserted);
        } else {
//-------- here i switch -----
//                          ||
//                          \/
            this._insertToDbOnProcessCallback = this._tradesInsertToDbWithoutCheck; 
            
            this._insertToDbOnProcessCallback(trades);
        }
    }


And another example:

This one only the first call will need to check. And it will not for all the rest one.

            exchangeClient.onTradeFeedCallback = this._onFirstFeedTrade as OnTradeFeedCallback;
//---------------------^^ we set our ref callback (to first time only version)

            exchangeClient.streams[symbol as string_symbol] = 
                exchangeClient.client.ws.trades(
                    symbol, 
                    (trade) => {   //<------------ here the callback         
                        (exchangeClient.onTradeFeedCallback as OnTradeFeedCallback)(trade as Trade, workers, exchangeClient); 
//----------------------------^^^^ here calling our refCallback
                    }
                );

and on first time version method

    private _onFirstFeedTrade(trade: Trade, workers: Worker[], exchangeClient: ExchangeClientObject) {
        /**
         * this run only once
         */
        //_______________alter callback function (this no more will be called)
        exchangeClient.onTradeFeedCallback = this._onTradeFeed; 
// do some things

// next time this._onTradeFeed will be called 

I think by now the idea is clear.

And here how it can be done in python

callback = None
def version1(index):
    global callback
    print('im version 1')
    if index == 5:
        callback = version2 // <---- switch upon that condition

def version2(index):
    global callback
    print('im vesrion 2')

callback = version1

for i in range(0,20):
    callback(i)

And here the running result: enter image description here

Cpu, compilers and Branching

And to finish we need to bring in the branch predictor and how it works. And why branch can be bad. And why the predictor can do great things. And recent cpu do great job.

To not go long on that here links about the subject

https://stackoverflow.com/a/11227902/5581565

What do compilers do with compile-time branching?

https://stackoverflow.com/a/32581909/7668448

To not make the list even longer i will stop at that. And when it come to using the if else within a callback that get called many many times or a loop. If after a certain time it only keep executing on one branch. I think that the branch predictor using the run statistics will optimize for the branch that is keeping running. Then it may just not matter at all this way. I will investigate the matter even farther and do some benchmarks and then i will update the answer. But that point is something to be aware of or consider.

I hope this was helpful. Happy coding.

Community
  • 1
  • 1
Mohamed Allal
  • 17,920
  • 5
  • 94
  • 97
1

In your case Valentino's suggestion to use 'if' statements makes much more sense.

However, there are some cases where you can't or don't want to hard-code every possible option (e.g. too many options, or the user should be able to define custom options). This is similar to the Strategy Pattern

Then you can explicitly provide the necessary methods to the class.

def main(postprocessing=None):
   ...
   while True:
      ...
      if postprocessing:
          for image in images: 
               postprocessing(image)

You can then call it for example with: main(postprocessing=video_in.write)

If you want to, you can also support a list of steps in the postprocessing arguments, to remain super flexible. I didn't do that here to keep the code simpler.

A disadvantage of this strategy is that every postprocessing step should use the same API. So your show method would not work out of the box. You would have to use something like:

main(postprocessing = partial(Show.show_win, 'in'))

This of course adds an extra layer of complexity. So for your case, repeating if statements will be much clearer.

Ivo Merchiers
  • 1,589
  • 13
  • 29
0

I'd pass the function two booleans, like so:

def loop(export=False, show=False):
    while True:
        ...
        for image in images:
            if export:
                video_in.write(image) # preparation for export
            if show:
                Show.show_win('in', image) # show input
        ...

Now you can still write your three convenience functions, like this:

def loop_show():
    return loop(show=True)

def loop_export():
    return loop(export=True)

def loop_show_export():
    return loop(show=True, export=True)

Or using partial:

from functools import partial

loop_show = partial(loop, show=True)
loop_export = partial(loop, export=True)
loop_show_export = partial(loop, export=True, show=True)
isaactfa
  • 5,461
  • 1
  • 10
  • 24
0

As most people answered it can be better to just not use the more complex way because an if statement does not need a lot of time. However it is possible to interchange a function.

I found an example here: python how to change functionality of a method during runtime

class Dog:
    def talk(self):
        print "bark"
dog1 = Dog()
dog1.talk()  # --> Bark

def cat_talk(self):
    print "Meow"

Dog.talk = cat_talk
dog1.talk()  # --> Meow

So I could use it to exchange my main method with either loop(), loop_show() ,... and so on but that is generally frowned upon because it might make the code less readable. Also it might not give such a high yield because as @Mohamed Allal suggested the branch predictor might just optimize for the branch that is used all the time anyway.

le_lemon
  • 107
  • 1
  • 11