我知道推迟一个过程。我还阅读了在处理文件和其他资源时使用它的好例子。我的问题是,它是不是像Python等其他语言处理的东西?如果是,Go如何提供一些独特的功能?
假设有两个函数f1
和f2
,我想在f2
完成时调用f1
。我可以这样做:
def f1():
done = False
print('I am doing extensive work')
done = True
return done
def f2():
print('here we go')
if f1():
f2()
通过尽量减少编码工作,推迟做同样的事情吗?
答案 0 :(得分:2)
defer
顾名思义,将延迟执行延迟函数,直到调用函数体退出,无论退出点和条件。因此,即使f1
内的某些内容因异常而失败,您的f2
deffering f2
函数也会确保调用f1
。我认为它是try / catch块中的finally
子句。
在python中,我能想到的最接近defer
的{{3}}是context managers。注意我怎么说最接近而且不相同,因为他们的目的有点重叠,但只有一点
答案 1 :(得分:1)
Go {'} defer statement只是另一种方法,它有其优点和缺点(不一定是最好的)。其他语言确实提供了处理适当资源关闭的替代方案例如,Java提供了例外和try-catch
块。
引自Go FAQ: Why does Go not have exceptions?
我们认为将异常耦合到控制结构(如
try-catch-finally
惯用法)会导致错综复杂的代码。它还倾向于鼓励程序员标记太多普通错误,例如无法打开文件,这是特殊的。Go采用不同的方法。对于简单的错误处理,Go的多值返回可以轻松报告错误而不会使返回值超载。 A canonical error type, coupled with Go's other features,使错误处理变得愉快,但与其他语言完全不同。
Go还有一些内置函数可以从真正特殊的条件发出信号并从中恢复。恢复机制仅作为在错误之后被拆除的功能状态的一部分执行,这足以处理灾难但不需要额外的控制结构,并且当使用良好时,可以产生干净的错误处理代码。有关详细信息,请参阅Defer, Panic, and Recover文章。
与Java defer
相比,使用try-catch
的优势是,处理已关闭或处置已打开资源的代码是紧邻代码旁边的代码打开它。
例如:
f, err := os.Open("file.txt")
if err != nil {
log.Printf("Failed to open file: %v", err)
return
}
defer f.Close()
// f is open you may read from it
...
如果处理资源的其他语言被移动到另一个代码块,那么在实际处理它的情况下更难以遵循它。
答案 2 :(得分:1)
要理解延迟,您需要了解替代方案。
在处理许多资源的复杂代码中,你最终会做这样的事情(伪代码):
mtx.Lock()
file1, err := os.Open(...)
if err != nil {
mtx.Unlock()
return err
}
conn, err := net.Dial(...)
if err != nil {
file1.Close()
mtx.Unlock()
return err
}
file2, err := os.Open(...)
if err != nil {
conn.Close()
file1.Close()
mtx.Unlock()
return err
}
do_things()
file2.Close()
conn.Close()
file1.Close()
mtx.Unlock()
return nil
这速度很快。此外,从长远来看,它很容易出错,因为有人编辑代码并且可能在打开file1之前将网络连接的开放移动可能无法正确更新所有错误处理路径。
在C中,这里常见的习惯是在函数末尾收集所有资源的清理,并执行以下操作:
ret = ERROR;
lock(mtx);
fd1 = open(...);
if (fd1 == -1)
goto err1;
conn = net_conn(...);
if (conn == -1)
goto err2;
fd2 = open(...);
if (fd2 == -1)
goto err3;
do_things();
ret = 0;
close(fd2);
err3:
close(conn);
err2:
close(fd1);
err1:
unlock(mtx);
return ret;
这也不是很好,也很容易弄错。
作为比较做同样的事情我们得到:
mtx.Lock()
defer mtx.Unlock()
file1, err := os.Open(...)
if err != nil {
return err
}
defer file1.Close()
conn, err := net.Dial(...)
if err != nil {
return err
}
defer conn.Close()
file2, err := os.Open(...)
if err != nil {
return err
}
defer file2.Close()
.... do things ...
return nil
清楚,简洁,任何了解Go的人都会理解会发生什么,并且很难在将来出错或打破。