Search code examples
pythongodefer-keywordlexical-cleanup

Python equivalent of golang's defer statement


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

Solution

  • 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