当前位置: 首页 > 知识库问答 >
问题:

如何在Python中获得函数的多个版本,而不复制粘贴函数的其余部分

何建中
2023-03-14

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

我不想在循环中输入一个“if”,每次我经历它时都会被问到,所以我想,我只是编写该函数的4个不同版本(循环,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
        ###############################################
        ...

共有3个答案

蔺弘
2023-03-14

在你的情况下,瓦伦蒂诺建议使用“如果”陈述更有意义。

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

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

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

例如,您可以使用< code>main(后处理=video_in.write)调用它

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

此策略的缺点是每个后处理步骤都应使用相同的 API。因此,您的显示方法不会开箱即用。您必须使用类似以下功能的内容:

main(后处理=部分(Show.Show_win,'in'))

这当然增加了额外的复杂性。所以对于你的情况,重复if语句会更清楚。

程彭祖
2023-03-14

太酷了。我很明白你的意思。我最近刚刚经历过。如果在检查完成并切换到另一个分支后将毫无用处,它将继续运行。

举个例子来解释这个问题。我们有一个方法或回调,只需要在第一次运行一些东西。或者在一些迭代次数之后。一旦切换,它将只在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 

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

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做得很好。

为了不走多远,这里的链接关于这个主题

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

编译器如何处理编译时分支?

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

为了不使这个列表变得更长,我就说到这里。以及在被多次调用回调或循环中使用if else。如果过了一段时间后,它只在一个分支上继续执行。我认为使用运行统计的分支预测器将为保持运行的分支进行优化。那么这样可能就一点都不重要了。我会进一步调查此事,做一些基准测试,然后我会更新答案。但这一点是需要注意或考虑的。

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

彭高畅
2023-03-14

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

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

将函数调用为< code>main()时,< code>show和< code>export参数默认为< code>False,因此循环中的两个< code>if不执行。< br >如果调用< code>main(show=True),则执行< code>if show:。< br >如果调用< code>main(export=True),则执行< code>if export:。< br >如果调用< code>main(show=True,export=True),则两个< code>if都执行。

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

 类似资料:
  • 函数功能:获得 LuaBox 当前版本 函数方法 version = device.getLuaBoxVer() 返回值 类型 说明 version string 返回获得 LuaBox 当前版本如:1.0 函数用例 version = device.getLuaBoxVer() dialog(version,5000) 注意事项 目前积木编程函数和触动精灵函数不通用,请仔细查看本手册,此手册中

  • 函数功能:获取系统版本号 函数方法 sysver = device.getOSVer() 返回值 类型 说明 sysver string 返回系统版本号如:7.0 函数用例 sysver = device.getOSVer(); --获取系统版本 dialog(sysver,5000) 注意事项 目前积木编程函数和触动精灵函数不通用,请仔细查看本手册,此手册中函数仅支持积木编程,不支持触动

  • 问题内容: 假设我有如下定义的Python函数: 我可以使用获取函数的名称。如上所述,我如何以编程方式获取其源代码? 问题答案: 如果该功能来自文件系统上可用的源文件,则可能会有帮助: 如果foo定义为: 然后: 返回值: 但是我相信,如果函数是从字符串,流中编译的,或者是从编译文件中导入的,那么您将无法检索其源代码。

  • 我需要瞄准地图函数中的所有组件,但我只得到其中的最后一个组件。

  • 问题内容: 我想从函数本身内部打印python函数的文档字符串。例如 目前,在定义之后,我将直接执行此操作。 但宁愿让函数自己执行此操作。 我已经尝试在my_function内调用它 ,但这没有用。 问题答案: 只要您不更改绑定到名称的对象,此方法就可以工作。 您会执行此操作的情况很少见,但确实会发生。 但是,如果您编写这样的装饰器: 现在您可以执行以下操作: 这将确保您的函数获得对自身的引用(类

  • 问题内容: 我想对Python中的函数进行深拷贝。该 副本 模块是没有帮助的,根据文件,其中说: 该模块不复制诸如模块,方法,堆栈跟踪,堆栈框架,文件,套接字,窗口,数组或任何类似类型的类型。它通过不变地返回原始对象来“复制”函数和类(浅层和深层)。这与泡菜模块处理这些食物的方式兼容。 我的目标是使两个函数具有相同的实现,但具有不同的文档字符串。 那怎么办呢? 问题答案: FunctionType