Python equivalent of golang defer expression

How to implement something that works as a defer statement from python?

Snooze a function call onto the stack. When the function containing the defer statement is returned, the calls to the deferred function are inverted and executed one after the other, in the area in which the defer statement originally lay. Deferred statements look like function calls, but are not executed until they are unloaded.

An example of using this method:

 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") } 

Outputs:

 counting done 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 

Usecase example:

 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. } 
+15
python go deferred
source share
5 answers

To emulate the defer fmt.Println(*a, i) example, you could 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") 

Exit

 counting done 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 

It is easy to imitate the mutex case:

 def some_function(lock=Lock()): with lock: # whatever 
+14
source share

I made one there (compatible with 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!") 

Source 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__ 
+8
source share

Python with an expression serves a similar purpose for Go defer.

Similar code in 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. 
+6
source share

A deferred implementation, partially inspired by @DenisKolodin 's answer , is available as part of pygolang , 2 :

  wc = wcfs.join(zurl) β”‚ wc = wcfs.join(zurl) defer(wc.close) β”‚ try: β”‚ ... ... β”‚ ... ... β”‚ ... ... β”‚ finally: β”‚ wc.close() 
+4
source share

This is an addition to jfs ExitStack 's answer to the ExitStack idea a bit further using decorators:

 @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 defined as follows:

 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 
0
source share

All Articles