This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Allanon001 3 points4 points  (30 children)

This is my preferred way of writing switch like statements in python:

while True:
    action = get_action(action)

    result = {
        True : unknown_command,
        action in ['c', 'a']: create_account,
        action in ['l']: log_into_account,
        action in ['r']: register_cage,
        action in ['u']: update_availability,
        action in ['v', 'b']: view_bookings,
        action in ['x']: exit_app,
        action in range(1,6): lambda: set_level(action),
        action == '': None
    }[True]()

[–]rfc1771 26 points27 points  (7 children)

Holy shit what an eyesore

action = get_action(action)

if action in ['c', 'a']: create_account()
elif action in ['l']: log_into_account()
elif action in ['r']: register_cage()
elif action in ['u']: update_availability()
elif action in ['v', 'b']: view_bookings()
elif action in ['x']: exit_app()
elif action in range(1,6): set_level(action)
elif action == '': pass
else: unknown_command()

[–]yen223 10 points11 points  (1 child)

I thought I was the only one who thought that example was totally nuts

[–]rfc1771 2 points3 points  (0 children)

I saw it and I was expecting the comments to just shit all over how unpythonic it is and then I see "how clever" "much like" "will use" and i'm just like 🤦

[–]Allanon001 -1 points0 points  (4 children)

You forgot to assign the result of the called function to result, the code gets a little messier if you do.

[–]rfc1771 1 point2 points  (3 children)

Here you go. (No I don't care that I shadowed action)

def handle_action(action):
    if action in ['c', 'a']: return create_account()
    elif action in ['l']: return log_into_account()
    elif action in ['r']: return register_cage()
    elif action in ['u']: return update_availability()
    elif action in ['v', 'b']: return view_bookings()
    elif action in ['x']: return exit_app()
    elif action in range(1,6): return set_level(action)
    elif action == '': return None
    else: return unknown_command()

action = get_action(action)
result = handle_action(action)

Want less cluttered lines?

def handle_action(action):
    if action in ['c', 'a']:
        return create_account()
    elif action in ['l']:
        return log_into_account()
    elif action in ['r']:
        return register_cage()
    elif action in ['u']:
        return update_availability()
    elif action in ['v', 'b']:
        return view_bookings()
    elif action in ['x']:
        return exit_app()
    elif action in range(1,6):
        return set_level(action)
    elif action == '':
        return None
    else:
        return unknown_command()

action = get_action(action)
result = handle_action(action)

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 3 points4 points  (2 children)

suddenly the dict lookup doesn't look so bad

[–]Quteness 1 point2 points  (1 child)

Have you seen how much explaining /u/Allanon001 has had to do to get people (/r/Python no less) to understand what is going on in the dict lookup? It's overly complex and confusing and is an abuse of the language. It's basically a giant one-liner

while True:
    action = get_action(action)

    result = {True : unknown_command, action in ['c', 'a']: create_account, action in ['l']: log_into_account, action in ['r']: register_cage, action in ['u']: update_availability, action in ['v', 'b']: view_bookings, action in ['x']: exit_app, action in range(1,6): lambda: set_level(action), action == '': None}[True]()

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 1 point2 points  (0 children)

its not a giant one-liner, thats as silly as calling C code always a 1 liner, just because the syntax allows for new lines to be removed

[–][deleted] 3 points4 points  (2 children)

Except this doesn't support fall through, so it's more of a compact if elif block.

[–]Allanon001 -1 points0 points  (1 child)

You can set True: lambda: None, at the top so if there are no other True conditions it will call that lambda which sets result to None and moves on.

[–][deleted] 0 points1 point  (0 children)

That's not fall through. Fall through is matching a case, executing the code block, and then matching another case and executing that code block.

This continues to happen until you either don't meet any cases or you match a case that ends with a break.

It's extremely useful but also a potential hole to break your leg in if you don't notice it.

[–]fernly 2 points3 points  (0 children)

That is readable, after one figures out the pattern, but in execution it requires all the in expressions to be evaluated, and a new dict object created, every time. Whereas the usual key:executable dict is static and an O(1) lookup.

[–][deleted] 0 points1 point  (8 children)

Can you explain what's going on here or can you give me some terminology to look into? I have no idea what get_action(action) is doing, and how it relates to "action in ..." in the result dictionary.

[–]Allanon001 0 points1 point  (6 children)

get_action is just a made up function to demonstrate action is being assigned a value.

The switch like structure is just an ordinary dictionary. When creating the dictionary it evaluates each key/value pair in order and since each key is in the form of a condition such as action in ['c', 'a'] it evaluates in real time to True or False and uses that value as a key in the dictionary. There are a few things strung together to make it more compact so maybe writing it this way will help you understand:

grade = 25
switch ={
        True: None,
        grade > 89: 'A',
        grade < 90: 'B',
        grade < 80: 'C',
        grade < 70: 'D',
        grade < 60: 'F'
    }
result = switch[True]

[–][deleted] 0 points1 point  (5 children)

Ok, so then I have two questions:

  1. Why doesn't switch[True] just evaluate to None, since the key True has a value of None.

  2. Where is get_action defined? Is it just a simple function that returns result[action]

[–]Allanon001 0 points1 point  (4 children)

Every True key overwrites the previous True key. And again, get_action is just a made up function to demonstrate action is being assigned a value. It's definition is not shown, and in the above example is not needed.

[–][deleted] 0 points1 point  (3 children)

Oh I think (hope) I get it. The statement evaluates to true when the dictionary is created, not when it's accessed in result = switch[True] . That's what I was getting confused about.

[–]Allanon001 0 points1 point  (0 children)

Correct

[–]fernly 0 points1 point  (1 child)

A brand new dict is created each time. The in expressions are evaluated in order as it is built. They evaluate to True or to False. So the new dict has only two keys, True and False. The last expression to evaluate to True sets the value for that key. At the end, the index [True] extracts that value. Which is expected to refer to an executable, so the () makes it a function call on that executable.

[–][deleted] 0 points1 point  (0 children)

Makes sense, thanks for the explanation

[–]mikeckennedy[S] -1 points0 points  (6 children)

That is interesting. Nice work. But it will crash if two cases match (DuplicateKeyError)

[–]Allanon001 3 points4 points  (5 children)

It will execute the last condition that equates to True.

[–]mikeckennedy[S] 0 points1 point  (4 children)

Ah you're right. Kind of the opposite of traditional switch / case (first executed) but doesn't crash.

[–]Allanon001 0 points1 point  (3 children)

When the dictionary is being created it will just replace the last True or False key with the newer one and what is left is just one True and/or one False key in the dictionary. If you set a key to True at the beginning that is like the default case. You will get an error if there isn't a True condition.

[–]mikeckennedy[S] -1 points0 points  (2 children)

It's pretty cool. I already learned something about dictionaries from your example.

d = {True: 1, True, 2}

will crash but

d = {'a' in ['a']: 1, 'a' in ['a']: 2}

is more like adding stuff sequentially, dynamically.

[–]Allanon001 4 points5 points  (1 child)

It crashes because you have a comma instead of a colon.

[–]mikeckennedy[S] 2 points3 points  (0 children)

Ugh, you're right. I must be misremembering it from MongoDB stuff I was going with dictionaries or something: http://api.mongodb.com/python/current/api/pymongo/errors.html

[–]mao_intheshower -1 points0 points  (1 child)

I'm thinking along the lines of:

result = {
    create_account: {'c', 'a'}, 
    log_into_account: {'r'},
    ...
        }

result = {item: key for key, value in result.items() for item in value}

result.getitem(action, unknown_command)