Lazy evaluation of strings in python logging: comparing `%` with `.format`

Viewed 13457

Is there any difference between these two calls:

import logging

logging.getLogger().debug('test: %i' % 42)

and

logging.getLogger().debug('test: {}'.format(42))

Where we assume 42 is replaced by some long computation (say 7.5 million years of computation) when it is cast to a string that produces a final answer of 42.

Is the first approach lazily evaluated in case logging is set to debug?

2 Answers

Neither are lazy. Both strings are interpolated before sent to logger. Lazy evaluation in terms of python logging is done with separate arguments. The documentation https://docs.python.org/2/library/logging.html suggest the following for lazy evaluation of string interpolation;

logging.getLogger().debug('test: %i', 42)

TL;DR In this case it’s easier to consider the following. We sent a primitive type (string) but only one argument to the logger. Thus it can’t be lazy.

I'd take a look in the references I posted in the comments for more detailed information on % and .format().

For the lazy evaluation question, the answer is no.

A simple test will do

def func1(x):
    time.sleep(5)
    return(x)

def func2(x):
    #time.sleep(5)
    return(x)

%timeit 'debug1: %s' % func1(3)
5 s ± 1.31 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit 'debug2: {}'.format(func1(3))
5 s ± 1.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit 'debug1: %s' % func2(3)
297 ns ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit 'debug2: {}'.format(func2(3))
404 ns ± 4.56 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In both .format (obviously) and % approaches,func() is calculated anyway.

Related