Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically building a Boolean expression

I'm writing some code that increments the number at the end of a filename until it's no longer overwriting an existing file. I'm creating several files, all with the same base filename but different extensions, none of which I want to overwrite.

Naive version:

prefix = 'hello0'
while os.path.exists(prefix + '.abc') or os.path.exists(prefix + '.def') or os.path.exists(prefix + '.ghi'):
    n = int(prefix[-1])
    prefix = prefix[:-1] + str(n + 1)  # I know this doesn't work when n reaches two digits; my full code involves a regular expression

The condition could obviously get very long and ugly when there are more than a couple of extensions. I abstracted this into a for loop.

My version:

prefix = 'hello0'
extensions = ('.abc', '.def', '.ghi')  # there could be even more than this
condition = True
while condition:
    condition = False
    # if any of the paths still exist, set the condition back to True
    for extension in extensions:
        if os.path.exists(prefix + extension):
            condition = True

    n = int(prefix[-1])
    prefix = prefix[:-1] + str(n + 1)

I still feel this to be a little bit clunky: it's not entirely clear what the while loop is actually testing. Is it possible to build a Boolean expression dynamically, rather than setting a Boolean value?

I think the following might work (I haven't tested it, I only thought of it while writing this!) but I don't think I should have to resort to eval:

prefix = 'hello0'
extensions = ('.abc', '.def', '.ghi')

test = 'False'
for extension in extensions:
    test += " or os.path.exists(prefix + '" + extension + "')"
while eval(test):
    n = int(prefix[-1])
    prefix = prefix[:-1] + str(n + 1)
like image 503
Benjamin Hodgson Avatar asked Sep 19 '12 15:09

Benjamin Hodgson


1 Answers

You might want to use the any() built-in with a generator:

while any(os.path.exists(prefix + extension) for extension in extensions):

    # then increment prefix and try again, as in your example code

This computes the True or False that you need with a simpler syntax.

In general, if I ever feel the temptation to use eval() in a dynamic language like Python, then it means that there is an important language feature that I need to learn about. Dynamic languages are supposed to make code beautiful without the disaster of trying to write and maintain code-that-writes-more-code — so you have done exactly the right thing here by asking about your approach.

like image 141
Brandon Rhodes Avatar answered Nov 15 '22 08:11

Brandon Rhodes