简体   繁体   中英

How can I use a dictionary to implement "switch/case", where evaluating the values could fail?

I am working on the following problem: https://www.codewars.com/kata/5266876b8f4bf2da9b000362/train/python

Implement a function likes:: [String] -> String, which must take in input array, containing the names of people who like an item. It must return the display text as shown in the examples:

 likes([]) # must be "no one likes this" likes(["Peter"]) # must be "Peter likes this" likes(["Jacob", "Alex"]) # must be "Jacob and Alex like this" likes(["Max", "John", "Mark"]) # must be "Max, John and Mark like this" likes(["Alex", "Jacob", "Mark", "Max"]) # must be "Alex, Jacob and 2 others like this"

Here is my solution:

def likes(names):
    length = len(names)
    switcher = {
        0: "no one likes this",
        1: f"{names} likes this", 
        2: f'{names[0]} and {names[1]} like this', 
        3: f'{names[0]}, {names[1]} and {names[2]} like this', 
        4: f'{names[0]}, {names[1]} and ({length} - 2) others like this'
    }
    return switcher.get(length)

But if I call it with a list containing fewer than 2 names, I get the IndexError:

2: f'{names[0]} and {names[1]} like this',
IndexError: list index out of range

Why can't I do it this way or if I can how do I get around this?

 length = len(names) switcher = { 0: "no one likes this", 1: f"{names} likes this", 2: f'{names[0]} and {names[1]} like this', 3: f'{names[0]}, {names[1]} and {names[2]} like this', 4: f'{names[0]}, {names[1]} and ({length} - 2) others like this' } return switcher.get(length)

Here, presumably you were expecting that this behaves like a chained if / elif statement like this:

if length == 0:
    return "no one likes this"
elif length == 1:
    return f"{names} likes this"
elif length == 2:
    return f'{names[0]} and {names[1]} like this'

and so on, where each return expression is only evaluated if the corresponding condition is true.

But that is not the case. The dictionary switcher is built completely before the length comes into play. So when passing a list names with less than 2 elements, the values for 2, 3, and 4 elements are still evaluated, which raises an IndexError , since names[1] , or even names[0] do not exist.

The clearest way around this is just using if / elif / else .


You could create a dictionary containing functions which in turn return the formatted strings.

The "else" case should not be in the 4 key, but corresponds to the default value returned by .get() :

def likes(names):
    length = len(names)
    switcher = {
        0: lambda _: "no one likes this",
        1: lambda n: f"{n[0]} likes this", 
        2: lambda n: f'{n[0]} and {n[1]} like this', 
        3: lambda n: f'{n[0]}, {n[1]} and {n[2]} like this'
    }
    formatter = switcher.get(
        length,
        lambda n: f'{n[0]}, {n[1]} and ({length} - 2) others like this'
    )
    return formatter(names)

Note that in the 1 case you should use names[0] , not names .

The problem here is that the dictionary tries to create all its elements when you define switcher , and fails when lenght <= 2 since the third element ( names[2] ) does not exist and thus, it raises an error

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM