I have a debug logging method in my program that just prints the passed in string to sys.stderr if debug is True:
def debug_log(str):
if debug:
print(str, file=sys.stderr)
However, I'm currently passing in some f strings containing expressions that would be time-consuming computations once I go from my debugging data set with a few hundred items to my production dataset with a few million items. If I have a line in my code like:
debug_log(f'{sum([author_dict[a].get("count") for a in author_dict.keys()])} count total in author_dict')
am I correct to assume that that sum is going to get computed when I call debug_log, rather than only happening inside debug_log when it goes to actually do the print statement? And if that's the case is there a clean way to set this up so that it doesn't do the computation when debug is False? (Otherwise I'll just comment out the lines where that's going to matter before I run in production)
As pointed out in the comments, arguments are evaluated eagerly, f-strings included. To make the evaluation lazy, The logging
functions accept *args
and **kwargs
for example, as explained here.
Now, a clean way would be using strings that are subsequently format()
ed instead of f-strings, but if f-string are just too much convenient you can still use lambdas (only when lazy-evaluation is needed). You could change the debug_log
function in something like this:
def debug_log(log):
if not debug: return
str_log = log() if callable(log) and log.__name__ == '<lambda>' else log
print(str_log, file=sys.stderr)
A few examples should make everything more clear.
If you need to print an argument that does not need to be lazily evaluated, then nothing is different from before:
>>> debug = False
>>> debug_log('hello')
>>> debug = True
>>> debug_log('hello')
hello
Now suppose you have a function like this one:
def test():
print('please print me if necessary')
return 'done'
If called inside debug_log
as-is, then
>>> debug = False
>>> debug_log(test())
please print me if necessary
But, of course, you do not want to see please print me if necessary
because debug = False
. So, the clean way would be:
>>> debug = False
>>> debug_log(lambda: f'we are {test()}')
>>> debug = True
>>> debug_log(lambda: f'we are {test()}')
please print me if necessary
we are done
Basically, by enclosing an f-string inside a lambda, you can be sure that it is executed only when the lambda is actually called.