Python 相当于golang的defer语句
Python equivalent of golang's defer statement
如何实现类似于 python 中 go 中的 defer
语句的功能?
Defer 将函数调用推送到堆栈。当包含 defer 语句的函数 returns 时,延迟函数调用将在 defer 语句最初所在的范围内逐个弹出并执行。 Defer 语句看起来像函数调用,但在弹出之前不会执行。
运行示例:
func main() {
fmt.Println("counting")
var a *int
for i := 0; i < 10; i++ {
a = &i
defer fmt.Println(*a, i)
}
x := 42
a = &x
fmt.Println("done")
}
输出:
counting
done
9 9
8 8
7 7
6 6
5 5
4 4
3 3
2 2
1 1
0 0
Go 用例示例:
var m sync.Mutex
func someFunction() {
m.Lock()
defer m.Unlock()
// Whatever you want, with as many return statements as you want, wherever.
// Simply forget that you ever locked a mutex, or that you have to remember to release it again.
}
Python 的 with statement 与 Go 的 defer 具有相似的目的。
Python中类似的代码是:
mutex = Lock()
def someFunction():
with mutex:
# Whatever you want, with as many return statements
# as you want, wherever. Simply forget that you ever
# locked a mutex, or that you have to remember to
# release it again.
要模拟 defer fmt.Println(*a, i)
示例,您可以 use contextlib.ExitStack
:
#!/usr/bin/env python3
from contextlib import ExitStack
from functools import partial
print("counting")
with ExitStack() as stack:
for i in range(10):
a = i
stack.callback(partial(print, a, i))
x = 42
a = x
print("done")
Output
counting
done
9 9
8 8
7 7
6 6
5 5
4 4
3 3
2 2
1 1
0 0
模拟互斥情况很容易:
def some_function(lock=Lock()):
with lock:
# whatever
我做了一个there(兼容2.x):
@defers_collector
def func():
f = open('file.txt', 'w')
defer(lambda: f.close())
defer(lambda : print("Defer called!"))
def my_defer():
recover()
defer(lambda: my_defer())
print("Ok )")
panic("WTF?")
print("Never printed (((")
func()
print("Recovered!")
defers_collector
的来源是:
# Go-style error handling
import inspect
import sys
def panic(x):
raise Exception(x)
def defer(x):
for f in inspect.stack():
if '__defers__' in f[0].f_locals:
f[0].f_locals['__defers__'].append(x)
break
def recover():
val = None
for f in inspect.stack():
loc = f[0].f_locals
if f[3] == '__exit__' and '__suppress__' in loc:
val = loc['exc_value']
loc['__suppress__'].append(True)
break
return val
class DefersContainer(object):
def __init__(self):
# List for sustain refer in shallow clone
self.defers = []
def append(self, defer):
self.defers.append(defer)
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
__suppress__ = []
for d in reversed(self.defers):
try:
d()
except:
__suppress__ = []
exc_type, exc_value, traceback = sys.exc_info()
return __suppress__
def defers_collector(func):
def __wrap__(*args, **kwargs):
__defers__ = DefersContainer()
with __defers__:
func(*args, **kwargs)
return __wrap__
A defer 实现部分灵感来自 @DenisKolodin is available as part of pygolang, 2:
wc = wcfs.join(zurl) │ wc = wcfs.join(zurl)
defer(wc.close) │ try:
│ ...
... │ ...
... │ ...
... │ finally:
│ wc.close()
这个对 的补充在装饰器的帮助下进一步推动了 ExitStack
想法:
@with_exit_stack
def counting(n, stack):
for i in range(n):
stack.callback(print, i)
@with_exit_stack
def locking(lock, stack):
stack.enter_context(lock)
# whatever
with_exit_stack
定义如下:
import functools
import contextlib
def with_exit_stack(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with contextlib.ExitStack() as stack:
return func(*args, **kwargs, stack=stack)
return wrapper
为了好玩,我试着做了一个等价物(仅作为概念验证进行测试)
这是:
import os
import inspect
class defer:
"""
Proof of concept for a python equivalent of golang's defer statement
Note that the callback order is probably not guaranteed
"""
def __init__(self, callback, *args, **kwargs):
self.callback = callback
self.args = args
self.kwargs = kwargs
# Add a reference to self in the caller variables so our __del__
# method will be called when the function goes out of scope
caller = inspect.currentframe().f_back
caller.f_locals[b'_' + os.urandom(48)] = self
def __del__(self):
self.callback(*self.args, **self.kwargs)
用法示例:
def main():
first()
second()
def first():
print('- first')
defer(lambda: print(' - deferred'))
print('- first exit')
def second():
print('- second')
if __name__ == '__main__':
main()
如何实现类似于 python 中 go 中的 defer
语句的功能?
Defer 将函数调用推送到堆栈。当包含 defer 语句的函数 returns 时,延迟函数调用将在 defer 语句最初所在的范围内逐个弹出并执行。 Defer 语句看起来像函数调用,但在弹出之前不会执行。
运行示例:
func main() {
fmt.Println("counting")
var a *int
for i := 0; i < 10; i++ {
a = &i
defer fmt.Println(*a, i)
}
x := 42
a = &x
fmt.Println("done")
}
输出:
counting
done
9 9
8 8
7 7
6 6
5 5
4 4
3 3
2 2
1 1
0 0
Go 用例示例:
var m sync.Mutex
func someFunction() {
m.Lock()
defer m.Unlock()
// Whatever you want, with as many return statements as you want, wherever.
// Simply forget that you ever locked a mutex, or that you have to remember to release it again.
}
Python 的 with statement 与 Go 的 defer 具有相似的目的。
Python中类似的代码是:
mutex = Lock()
def someFunction():
with mutex:
# Whatever you want, with as many return statements
# as you want, wherever. Simply forget that you ever
# locked a mutex, or that you have to remember to
# release it again.
要模拟 defer fmt.Println(*a, i)
示例,您可以 use contextlib.ExitStack
:
#!/usr/bin/env python3
from contextlib import ExitStack
from functools import partial
print("counting")
with ExitStack() as stack:
for i in range(10):
a = i
stack.callback(partial(print, a, i))
x = 42
a = x
print("done")
Output
counting
done
9 9
8 8
7 7
6 6
5 5
4 4
3 3
2 2
1 1
0 0
模拟互斥情况很容易:
def some_function(lock=Lock()):
with lock:
# whatever
我做了一个there(兼容2.x):
@defers_collector
def func():
f = open('file.txt', 'w')
defer(lambda: f.close())
defer(lambda : print("Defer called!"))
def my_defer():
recover()
defer(lambda: my_defer())
print("Ok )")
panic("WTF?")
print("Never printed (((")
func()
print("Recovered!")
defers_collector
的来源是:
# Go-style error handling
import inspect
import sys
def panic(x):
raise Exception(x)
def defer(x):
for f in inspect.stack():
if '__defers__' in f[0].f_locals:
f[0].f_locals['__defers__'].append(x)
break
def recover():
val = None
for f in inspect.stack():
loc = f[0].f_locals
if f[3] == '__exit__' and '__suppress__' in loc:
val = loc['exc_value']
loc['__suppress__'].append(True)
break
return val
class DefersContainer(object):
def __init__(self):
# List for sustain refer in shallow clone
self.defers = []
def append(self, defer):
self.defers.append(defer)
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
__suppress__ = []
for d in reversed(self.defers):
try:
d()
except:
__suppress__ = []
exc_type, exc_value, traceback = sys.exc_info()
return __suppress__
def defers_collector(func):
def __wrap__(*args, **kwargs):
__defers__ = DefersContainer()
with __defers__:
func(*args, **kwargs)
return __wrap__
A defer 实现部分灵感来自 @DenisKolodin
wc = wcfs.join(zurl) │ wc = wcfs.join(zurl)
defer(wc.close) │ try:
│ ...
... │ ...
... │ ...
... │ finally:
│ wc.close()
这个对 ExitStack
想法:
@with_exit_stack
def counting(n, stack):
for i in range(n):
stack.callback(print, i)
@with_exit_stack
def locking(lock, stack):
stack.enter_context(lock)
# whatever
with_exit_stack
定义如下:
import functools
import contextlib
def with_exit_stack(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with contextlib.ExitStack() as stack:
return func(*args, **kwargs, stack=stack)
return wrapper
为了好玩,我试着做了一个等价物(仅作为概念验证进行测试)
这是:
import os
import inspect
class defer:
"""
Proof of concept for a python equivalent of golang's defer statement
Note that the callback order is probably not guaranteed
"""
def __init__(self, callback, *args, **kwargs):
self.callback = callback
self.args = args
self.kwargs = kwargs
# Add a reference to self in the caller variables so our __del__
# method will be called when the function goes out of scope
caller = inspect.currentframe().f_back
caller.f_locals[b'_' + os.urandom(48)] = self
def __del__(self):
self.callback(*self.args, **self.kwargs)
用法示例:
def main():
first()
second()
def first():
print('- first')
defer(lambda: print(' - deferred'))
print('- first exit')
def second():
print('- second')
if __name__ == '__main__':
main()