Search code examples
pythonperformanceformatlist-comprehension

Is there a better way to do 'complex' list comprehension?


I tend to use list comprehension a lot in Python because I think it is a clean way to generate lists, but often I find myself coming back a week later and thinking to myself "What the hell did I do this for?!" and it's a 70+ character nested conditional list comprehension statement. I am wondering if it gets to a certain point if I should break it out into if/elif/else, and the performance impact, if any of doing so.

My current circumstance:

Returned structure from call is a list of tuples. I need to cast it to a list, some values need to be cleaned up, and I need to strip the last element from the list.

e.g.

[(val1, ' ', 'ChangeMe', 'RemoveMe),
 (val1, ' ', 'ChangeMe', 'RemoveMe),
 (val1, ' ', 'ChangeMe', 'RemoveMe)]

So in this case, I want to remove RemoveMe, replace all ' ' with '' and replace ChangeMe with val2. I know it is a lot of changes, but the data I am returned is terrible sometimes and I have no control over what is coming to me as a response.

I currently have something like:

response = cursor.fetchall()
response = [['' if item == ' ' else item if item != 'ChangeMe' else 'val2' for item in row][:-1] for row in response]`

Is a nested multi-conditional comprehension statement frowned upon? I know stylistically Python prefers to be very readable, but also compact and not as verbose.

Any tips or info would be greatly appreciated. Thanks all!


Solution

  • Python favors one-liner, on the sole condition that these make the code more readable, and not that it complicates it.

    In this case, you use two nested list comprehension, two adjacent ternary operators, a list slicing, all of this on a single line which exceeds the 100 characters... It is everything but readable.

    Sometimes it's better to use a classic for loop.

    result = []
    for val, space, item, remove in response:
        result.append([val, '', 'val2'])
    

    And then you realise you can write it as a list comprehension much more comprehensible (assuming your filter condition is simple):

    result = [[val, '', 'val2'] for val, *_ in response]
    

    Remember, every code is written once, but it is read many times.