I recently encountered some python code that made frequent use of locals(), in a way that I considered bad form. It offers no performance benefit, and makes the code less readable.

It was a series of Django views that all looked a bit like this (with varying degrees of complexity):

def my_view(request):
    foo = get_foo()
    bar = get_bar()
    render('some_template.html', request, locals())

This pattern makes it difficult to work out which local variables are actually used by the template, and which are redundant. If an intermediate variable is created, for the sake of clarity or performance, that variable is then unnecessarily passed to the template. In a short, simple function like this, it isn’t that important, but in anything longer or more complex, it might start to cause problems.

However, it did make me wonder, was there a reason for this pattern? In the above case, it would be clearer were it be written as a dict literal, thus:

def my_view(request):
    render(
        'some_template.html',
        request,
        {'foo': get_foo(), 'bar': get_bar()}
    )

This rewrite could make it slightly more difficult to use an interactive debugger like pdb. That said, it is only a minor inconvenience.

Some of the views followed a pattern whereby one of the variables was used to look up the others, thus:

def my_view(request):
    foo = get_foo()
    bar = get_bar(foo)
    render('some_template.html', request, locals())

Still, the loss of clarity over what the template expects is harmful, so this pattern should be avoided.

def my_view(request):
    foo = get_foo()
    bar = get_bar(foo)
    render(
        'some_template.html',
        request,
        {'foo': foo, 'bar': get_bar(foo)}
    )

Checking the performance of the possible different forms yields no surprises in the results, locals() vs. a dict literal (without local variabls) are pretty much identical.

python -m timeit "def fn(): a=1; b=2; c=3; return locals()" "fn()"
1000000 loops, best of 3: 0.459 usec per loop

python -m timeit "def fn(): return {'a':1, 'b':2, 'c':3}" "fn()"
1000000 loops, best of 3: 0.464 usec per loop

Manually turning all the local variables into the same dict that would be returned by locals(), is slightly slower.

python -m timeit "def fn(): a=1; b=2; c=3; return {'a':a,'b':b,'c':c}" "fn()"
1000000 loops, best of 3: 0.559 usec per loop

However, that extra time decreases if a hybrid approach is taken, and fewer local variables are created:

python -m timeit "def fn(): a=1; return {'a':a,'b':2,'c':3}" "fn()"
1000000 loops, best of 3: 0.493 usec per loop

As well as the extra readability gained from explicitly defining the dictionary, the extra control can be advantageous for performance. If some of the local variables are not required in the final dictionary, then omitting them reduces the time in function even more (or rather, the surplus dict entries generated by locals() increases the time taken, over only doing what is necessary).

python -m timeit "def fn(): a=1; c=3; return {'a':a,'b':2}" "fn()"
1000000 loops, best of 3: 0.471 usec per loop

So, the only situation in which this use of locals() performs better than an explicit dict, is where a large number of the locals are to be used, and most of them can’t be set in the same line (i.e. because each value is used to generate another). However, the difference in performance is negligible, and unlikely to be worth the sacrifice of control and readability.