Search code examples
pythonlambda

Refactor a if-elif-block into a pythonic dictionary


I have a big if-elif-else block in my code like this

if counted == 2:
    score = None

elif counted == 1:
    score = (sum(values) - 3) / 6 * 100

elif counted == 0:
    score = (sum(values) - 4) / 8 * 100

else:
    raise Exception('Should not be reached!')

With python I assume there is a way to solve that with a dict using the counted values as keys. But what comes as items into that dict? The new case expression is not an option here. IMHO this wouldn't be pythonic no matter that it's now part of Python standard.

Approach A

# Approach A
mydict = {
    0: ((sum(values) - 4) / 8 * 100),
    1: ((sum(values) - 3) / 6 * 100),
    2: None
}

print(mydict)
print(mydict[counted])

The problem here is that all items are executed.

{0: 4625.0, 1: 6183.333333333334, 2: None}

Approach B

mydict = {
    0: (lambda _: (sum(values) - 4) / 8 * 100),
    1: (lambda _: (sum(values) - 3) / 6 * 100),
    2: (lambda _: None)
}

print(mydict)
print(mydict[counted](None))

Here I have the fake argument I don't need. But it seems to be mandatory when creating a lambda.

{0: <function <lambda> at 0x7ff8f3dce040>, 1: <function <lambda> at 0x7ff8f3b6a670>, 2: <function <lambda> at 0x7ff8f3b6a700>}

Is there a another way?

Full MWE

#!/usr/bin/env python3
import random

values = random.choices(range(100), k=10)
counted = random.choice(range(3))

print(f'counted={counted}')

if counted == 2:
    score = None

elif counted == 1:
    score = (sum(values) - 3) / 6 * 100

elif counted == 0:
    score = (sum(values) - 4) / 8 * 100

else:
    raise Exception('Should not be reached!')

print(f'score={score}')

# Approach A
mydict = {
    0: ((sum(values) - 4) / 8 * 100),
    1: ((sum(values) - 3) / 6 * 100),
    2: None
}

print(mydict)
print(mydict[counted])

# Approach B
mydict = {
    0: (lambda _: (sum(values) - 4) / 8 * 100),
    1: (lambda _: (sum(values) - 3) / 6 * 100),
    2: (lambda _: None)
}

print(mydict)
print(mydict[counted](None))

Solution

  • The lambdas without the arguments works. Just needs the :

    import random
    
    
    values = random.choices(range(100), k=10)
    
    mydict = {
        0: lambda: (sum(values) - 4) / 8 * 100,
        1: lambda: (sum(values) - 3) / 6 * 100,
        2: lambda: None
    }
    
    counted = random.choice(range(3))
    mydict[counted]()