【问题标题】:How to get multiple versions of a function without copy-pasting the rest of it in Python如何在不复制粘贴其余部分的情况下在 Python 中获取函数的多个版本
【发布时间】:2020-02-07 19:46:37
【问题描述】:

我有一个带有 while 循环的函数。 在我开始该功能之前,我希望能够决定是每次循环时都导出图像还是只想显示它,两者都做还是都不做。

我不想在每次通过它时都会询问的循环中添加一个“if”,所以我想我只写该函数的 4 个不同版本(loop、loop_show、loop_export、loop_show_export)怎么样. 但是我认为必须有一种方法可以不复制粘贴其余功能。

有没有办法做到这一点? 或者有没有更好的装饰器或其他方法?

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
        ###############################################
        ...

【问题讨论】:

  • 让你的函数接受一个参数来在几个选项之间切换。如果您向我们展示此功能(edit您的帖子),我们可以提供更好的帮助
  • 由于检查布尔值是一项几乎不需要时间的操作,所以除了if 之外的所有内容都是????
  • 在循环中添加if,不要试图智取自己。复制/粘贴4个类似的功能绝对不是要走的路。
  • @Valentino 是否为你添加了重要的sn-p功能

标签: python


【解决方案1】:

如果我理解正确,这就是你需要的:

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
        ###############################################
        ...

当以main() 调用您的函数时,showexport 参数默认为False,因此不会执行循环中的两个if
如果您调用main(show=True),则会执行if show:
如果您调用main(export=True),则会执行if export:
如果你调用main(show=True, export=True) 两个if 都会执行。

正如人们在评论中所说,检查布尔值几乎不需要时间,代码可读并且几乎相等的函数中没有重复的行。无需寻找更详尽的解决方案。

【讨论】:

    【解决方案2】:

    这太酷了。我非常明白你的意思。我最近才经历过。 检查完成后 if 将无用,您切换到另一个分支,它将继续运行。

    例如这里有一个例子来解释这个问题。想象一下,我们有一个方法或回调,只需要在第一次运行某些东西。或者经过一些迭代。一旦切换,它将仅在 else 或第二个代码上继续运行。

    所以有一个无用的 if 是一个无用的开销。如果能完全切换方法就好了。

    这里是如何在 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);
    
    

    这是一个真实的例子:

        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);
            }
        }
    
    
    

    还有一个例子:

    这只有第一次调用需要检查。它不会适用于所有其他人。

                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
                        }
                    );
    
    

    和第一次版本方法

        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 
    

    我想现在这个想法已经很清楚了。

    这里是如何在 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)
    

    这里是运行结果:

    CPU、编译器和分支

    最后,我们需要引入分支预测器及其工作原理。以及为什么分支可能不好。以及为什么预测器可以做伟大的事情。最近的 cpu 做得很好。

    关于这个主题的链接不要太长

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

    What do compilers do with compile-time branching?

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

    为了不让列表更长,我会停止。 当涉及到在多次调用的回调或循环中使用 if else 时。如果在一段时间后它只在一个分支上继续执行。我认为使用运行统计信息的分支预测器将为继续运行的分支进行优化。那么它可能根本不重要。我将进一步调查此事并做一些基准测试,然后我会更新答案。但这一点是需要注意或考虑的。

    我希望这会有所帮助。编码愉快。

    【讨论】:

      【解决方案3】:

      在您的情况下,Valentino 建议使用“if”语句更有意义。

      但是,在某些情况下,您不能或不想对每个可能的选项进行硬编码(例如,选项太多,或者用户应该能够定义自定义选项)。这类似于Strategy Pattern

      然后你可以显式地为类提供必要的方法。

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

      然后您可以调用它,例如: main(postprocessing=video_in.write)

      如果您愿意,您还可以支持后处理参数中的步骤列表,以保持超级灵活。我在这里没有这样做以使代码更简单。

      此策略的一个缺点是每个后处理步骤都应使用相同的 API。所以你的 show 方法不能开箱即用。你必须使用类似的东西:

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

      这当然增加了额外的复杂性。因此,对于您的情况,重复 if 语句会更清晰。

      【讨论】:

        【解决方案4】:

        我会向函数传递两个布尔值,如下所示:

        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
                ...
        

        现在您仍然可以编写您的三个便利函数,如下所示:

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

        或者使用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)
        

        【讨论】:

          【解决方案5】:

          正如大多数人回答的那样,最好不要使用更复杂的方式,因为 if 语句不需要很多时间。但是,可以互换功能。

          我在这里找到了一个例子: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
          

          所以我可以使用它来与 loop()、loop_show() 等交换我的 main 方法,但这通常是不受欢迎的,因为它可能会降低代码的可读性。 此外,它可能不会产生如此高的产量,因为正如@Mohamed Allal 所建议的那样,分支预测器可能只是针对一直使用的分支进行优化。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-05-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-05-27
            • 1970-01-01
            相关资源
            最近更新 更多