Python equivalent of golang's defer statement

Filip Haglund picture Filip Haglund · Jan 6, 2016 · Viewed 13.4k times · Source

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:

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

Go example of a usecase:

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.
}

Answer

jfs picture jfs · Jan 6, 2016

To emulate 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")

Output

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 emulate the mutex case:

def some_function(lock=Lock()):
    with lock:
        # whatever