Pythonでdefer
ステートメントのように機能するものをどのように実装しますか?
Deferは関数呼び出しをスタックにプッシュします。 deferステートメントを含む関数が戻ると、deferステートメントが最初に含まれていたスコープ内で、遅延された関数呼び出しが1つずつポップされて実行されます。据え置きステートメントは関数呼び出しのように見えますが、ポップされるまで実行されません。
それがどのように機能するかの例を見てください:
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
ユースケースの例をご覧ください:
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.
}
defer fmt.Println(*a, i)
の例をエミュレートするには、次のようにします se 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")
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
Pythonの withステートメント は、Goの延期と同様の目的を果たします。
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.
私は1つ作りました 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__
@ DenisKolodinanswer から部分的に着想を得たdefer実装は の一部として利用できますpygolang 、 2 :
wc = wcfs.join(zurl) │ wc = wcfs.join(zurl)
defer(wc.close) │ try:
│ ...
... │ ...
... │ ...
... │ finally:
│ wc.close()
これは jfsの答え を補完するものであり、デコレータの助けを借りて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