Python中与Golang的defer语句相对应的是什么?

huangapple go评论122阅读模式
英文:

Python equivalent of golang's defer statement

问题

如何在Python中实现类似Go语言中的defer语句的功能?

defer语句将一个函数调用推入一个栈中。当包含defer语句的函数返回时,推迟的函数调用会被弹出并逐个执行,执行的作用域与defer语句所在的作用域相同。defer语句看起来像函数调用,但直到它们被弹出时才会执行。

以下是Go语言中的示例代码:

  1. func main() {
  2. fmt.Println("counting")
  3. var a *int
  4. for i := 0; i < 10; i++ {
  5. a = &i
  6. defer fmt.Println(*a, i)
  7. }
  8. x := 42
  9. a = &x
  10. fmt.Println("done")
  11. }

输出结果:

  1. counting
  2. done
  3. 9 9
  4. 8 8
  5. 7 7
  6. 6 6
  7. 5 5
  8. 4 4
  9. 3 3
  10. 2 2
  11. 1 1
  12. 0 0

Go语言中的用例示例:

  1. var m sync.Mutex
  2. func someFunction() {
  3. m.Lock()
  4. defer m.Unlock()
  5. // 无论你想做什么,可以有多个返回语句,随意放置。
  6. // 只需忘记你曾经锁定了互斥锁,或者记得要释放它。
  7. }

希望对你有帮助!

英文:

How would one implement something that works like the defer statement from go in python?

Defer pushes a function call to a stack. When the function containing the defer statement returns, the defered function calls are popped and executed one by one, in the scope that the defer statement was inside in the first place. Defer statements look like function calls, but are not executed until they are popped.

Go example of how it works:

  1. func main() {
  2. fmt.Println(&quot;counting&quot;)
  3. var a *int
  4. for i := 0; i &lt; 10; i++ {
  5. a = &amp;i
  6. defer fmt.Println(*a, i)
  7. }
  8. x := 42
  9. a = &amp;x
  10. fmt.Println(&quot;done&quot;)
  11. }

Outputs:

  1. counting
  2. done
  3. 9 9
  4. 8 8
  5. 7 7
  6. 6 6
  7. 5 5
  8. 4 4
  9. 3 3
  10. 2 2
  11. 1 1
  12. 0 0

Go example of a usecase:

  1. var m sync.Mutex
  2. func someFunction() {
  3. m.Lock()
  4. defer m.Unlock()
  5. // Whatever you want, with as many return statements as you want, wherever.
  6. // Simply forget that you ever locked a mutex, or that you have to remember to release it again.
  7. }

答案1

得分: 33

要模拟defer fmt.Println(*a, i)的示例,你可以使用contextlib.ExitStack。代码如下:

  1. #!/usr/bin/env python3
  2. from contextlib import ExitStack
  3. from functools import partial
  4. print("counting")
  5. with ExitStack() as stack:
  6. for i in range(10):
  7. a = i
  8. stack.callback(partial(print, a, i))
  9. x = 42
  10. a = x
  11. print("done")

输出结果如下:

  1. counting
  2. done
  3. 9 9
  4. 8 8
  5. 7 7
  6. 6 6
  7. 5 5
  8. 4 4
  9. 3 3
  10. 2 2
  11. 1 1
  12. 0 0

要模拟互斥锁的情况也很容易,代码如下:

  1. def some_function(lock=Lock()):
  2. with lock:
  3. # whatever

希望对你有帮助!

英文:

To emulate defer fmt.Println(*a, i) example, you could use contextlib.ExitStack:

  1. #!/usr/bin/env python3
  2. from contextlib import ExitStack
  3. from functools import partial
  4. print(&quot;counting&quot;)
  5. with ExitStack() as stack:
  6. for i in range(10):
  7. a = i
  8. stack.callback(partial(print, a, i))
  9. x = 42
  10. a = x
  11. print(&quot;done&quot;)

Output

  1. counting
  2. done
  3. 9 9
  4. 8 8
  5. 7 7
  6. 6 6
  7. 5 5
  8. 4 4
  9. 3 3
  10. 2 2
  11. 1 1
  12. 0 0

It is easy to emulate the mutex case:

  1. def some_function(lock=Lock()):
  2. with lock:
  3. # whatever

答案2

得分: 18

Python的with语句与Go的defer有类似的作用。

在Python中,相似的代码如下:

  1. mutex = Lock()
  2. def someFunction():
  3. with mutex:
  4. # 无论你想做什么,有多少个return语句,随时随地都可以。
  5. # 只需忘记你曾经锁定了互斥锁,或者需要记住再次释放它。
英文:

Python's with statement serves a similar purpose to Go's defer.

The similar code in Python is:

  1. mutex = Lock()
  2. def someFunction():
  3. with mutex:
  4. # Whatever you want, with as many return statements
  5. # as you want, wherever. Simply forget that you ever
  6. # locked a mutex, or that you have to remember to
  7. # release it again.

答案3

得分: 12

我已经制作了一个兼容2.x版本的代码(在此处1):

  1. @defers_collector
  2. def func():
  3. f = open('file.txt', 'w')
  4. defer(lambda: f.close())
  5. defer(lambda: print("Defer called!"))
  6. def my_defer():
  7. recover()
  8. defer(lambda: my_defer())
  9. print("Ok")
  10. panic("WTF?")
  11. print("Never printed ((((")
  12. func()
  13. print("Recovered!")

defers_collector的源代码如下:

  1. # Go-style error handling
  2. import inspect
  3. import sys
  4. def panic(x):
  5. raise Exception(x)
  6. def defer(x):
  7. for f in inspect.stack():
  8. if '__defers__' in f[0].f_locals:
  9. f[0].f_locals['__defers__'].append(x)
  10. break
  11. def recover():
  12. val = None
  13. for f in inspect.stack():
  14. loc = f[0].f_locals
  15. if f[3] == '__exit__' and '__suppress__' in loc:
  16. val = loc['exc_value']
  17. loc['__suppress__'].append(True)
  18. break
  19. return val
  20. class DefersContainer(object):
  21. def __init__(self):
  22. # List for sustain refer in shallow clone
  23. self.defers = []
  24. def append(self, defer):
  25. self.defers.append(defer)
  26. def __enter__(self):
  27. pass
  28. def __exit__(self, exc_type, exc_value, traceback):
  29. __suppress__ = []
  30. for d in reversed(self.defers):
  31. try:
  32. d()
  33. except:
  34. __suppress__ = []
  35. exc_type, exc_value, traceback = sys.exc_info()
  36. return __suppress__
  37. def defers_collector(func):
  38. def __wrap__(*args, **kwargs):
  39. __defers__ = DefersContainer()
  40. with __defers__:
  41. func(*args, **kwargs)
  42. return __wrap__
英文:

I've made one there (compatible with 2.x):

  1. @defers_collector
  2. def func():
  3. f = open(&#39;file.txt&#39;, &#39;w&#39;)
  4. defer(lambda: f.close())
  5. defer(lambda : print(&quot;Defer called!&quot;))
  6. def my_defer():
  7. recover()
  8. defer(lambda: my_defer())
  9. print(&quot;Ok )&quot;)
  10. panic(&quot;WTF?&quot;)
  11. print(&quot;Never printed (((&quot;)
  12. func()
  13. print(&quot;Recovered!&quot;)

Source of defers_collector is:

  1. # Go-style error handling
  2. import inspect
  3. import sys
  4. def panic(x):
  5. raise Exception(x)
  6. def defer(x):
  7. for f in inspect.stack():
  8. if &#39;__defers__&#39; in f[0].f_locals:
  9. f[0].f_locals[&#39;__defers__&#39;].append(x)
  10. break
  11. def recover():
  12. val = None
  13. for f in inspect.stack():
  14. loc = f[0].f_locals
  15. if f[3] == &#39;__exit__&#39; and &#39;__suppress__&#39; in loc:
  16. val = loc[&#39;exc_value&#39;]
  17. loc[&#39;__suppress__&#39;].append(True)
  18. break
  19. return val
  20. class DefersContainer(object):
  21. def __init__(self):
  22. # List for sustain refer in shallow clone
  23. self.defers = []
  24. def append(self, defer):
  25. self.defers.append(defer)
  26. def __enter__(self):
  27. pass
  28. def __exit__(self, exc_type, exc_value, traceback):
  29. __suppress__ = []
  30. for d in reversed(self.defers):
  31. try:
  32. d()
  33. except:
  34. __suppress__ = []
  35. exc_type, exc_value, traceback = sys.exc_info()
  36. return __suppress__
  37. def defers_collector(func):
  38. def __wrap__(*args, **kwargs):
  39. __defers__ = DefersContainer()
  40. with __defers__:
  41. func(*args, **kwargs)
  42. return __wrap__

答案4

得分: 9

一个部分受到@DenisKolodin回答启发的defer实现已作为pygolang的一部分可用,2

  1. wc = wcfs.join(zurl) wc = wcfs.join(zurl)
  2. defer(wc.close) try:
  3. ...
  4. ... ...
  5. ... ...
  6. ... finally:
  7. wc.close()
英文:

A defer implementation partly inspired by @DenisKolodin answer is available as part of pygolang, 2:

  1. wc = wcfs.join(zurl) wc = wcfs.join(zurl)
  2. defer(wc.close) try:
  3. ...
  4. ... ...
  5. ... ...
  6. ... finally:
  7. wc.close()

答案5

得分: 5

这是对jfs'答案的补充,借助装饰器进一步推动了ExitStack的概念:

  1. @with_exit_stack
  2. def counting(n, stack):
  3. for i in range(n):
  4. stack.callback(print, i)
  5. @with_exit_stack
  6. def locking(lock, stack):
  7. stack.enter_context(lock)
  8. # 其他操作

with_exit_stack的定义如下:

  1. import functools
  2. import contextlib
  3. def with_exit_stack(func):
  4. @functools.wraps(func)
  5. def wrapper(*args, **kwargs):
  6. with contextlib.ExitStack() as stack:
  7. return func(*args, **kwargs, stack=stack)
  8. return wrapper
英文:

This complement to jfs' answer pushes the ExitStack idea a bit further with the help of decorators:

  1. @with_exit_stack
  2. def counting(n, stack):
  3. for i in range(n):
  4. stack.callback(print, i)
  5. @with_exit_stack
  6. def locking(lock, stack):
  7. stack.enter_context(lock)
  8. # whatever

with_exit_stack is defined as follows:

  1. import functools
  2. import contextlib
  3. def with_exit_stack(func):
  4. @functools.wraps(func)
  5. def wrapper(*args, **kwargs):
  6. with contextlib.ExitStack() as stack:
  7. return func(*args, **kwargs, stack=stack)
  8. return wrapper

答案6

得分: 3

我尝试制作一个等效的代码(仅作为概念验证进行了测试)

以下是代码:

  1. import os
  2. import inspect
  3. class defer:
  4. """
  5. Proof of concept for a python equivalent of golang's defer statement
  6. Note that the callback order is probably not guaranteed
  7. """
  8. def __init__(self, callback, *args, **kwargs):
  9. self.callback = callback
  10. self.args = args
  11. self.kwargs = kwargs
  12. # Add a reference to self in the caller variables so our __del__
  13. # method will be called when the function goes out of scope
  14. caller = inspect.currentframe().f_back
  15. caller.f_locals[b'_' + os.urandom(48)] = self
  16. def __del__(self):
  17. self.callback(*self.args, **self.kwargs)
  18. Usage example:
  19. def main():
  20. first()
  21. second()
  22. def first():
  23. print('- first')
  24. defer(lambda: print(' - deferred'))
  25. print('- first exit')
  26. def second():
  27. print('- second')
  28. if __name__ == '__main__':
  29. main()

希望对你有帮助!

英文:

I've tried to make an equivalent for fun (only tested as a proof of concept)

Here is is:

  1. import os
  2. import inspect
  3. class defer:
  4. &quot;&quot;&quot;
  5. Proof of concept for a python equivalent of golang&#39;s defer statement
  6. Note that the callback order is probably not guaranteed
  7. &quot;&quot;&quot;
  8. def __init__(self, callback, *args, **kwargs):
  9. self.callback = callback
  10. self.args = args
  11. self.kwargs = kwargs
  12. # Add a reference to self in the caller variables so our __del__
  13. # method will be called when the function goes out of scope
  14. caller = inspect.currentframe().f_back
  15. caller.f_locals[b&#39;_&#39; + os.urandom(48)] = self
  16. def __del__(self):
  17. self.callback(*self.args, **self.kwargs)

Usage example:

  1. def main():
  2. first()
  3. second()
  4. def first():
  5. print(&#39;- first&#39;)
  6. defer(lambda: print(&#39; - deferred&#39;))
  7. print(&#39;- first exit&#39;)
  8. def second():
  9. print(&#39;- second&#39;)
  10. if __name__ == &#39;__main__&#39;:
  11. main()

答案7

得分: 0

我晚了一点,但我建立了一个可以通过pip安装的库来实现这个!请查看python-defer

  1. from defer import defer
  2. def foo():
  3. print("Hello, world!") in defer
  4. print("Hello", end="")
  5. # 做一些可能失败的事情...
  6. assert 1 + 1 == 3
  1. $ python foo.py
  2. Hello, World!
  3. Traceback (most recent call last):
  4. File "foo.py", line 7, in <module>
  5. assert 1 + 1 == 3
  6. AssertionError
英文:

Little late to the party but I built a pip-installable library that does just this! Check out python-defer.

  1. from defer import defer
  2. def foo():
  3. print(&quot;, world!&quot;) in defer
  4. print(&quot;Hello&quot;, end=&quot;&quot;)
  5. # do something that might fail...
  6. assert 1 + 1 == 3
  1. $ python foo.py
  2. Hello, World!
  3. Traceback (most recent call last):
  4. File &quot;foo.py&quot;, line 7, in &lt;module&gt;
  5. assert 1 + 1 == 3
  6. AssertionError

huangapple
  • 本文由 发表于 2016年1月6日 11:21:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/34625089.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定