-
-
Save mikeckennedy/23ebca9ee1afd29f4534539d0ad3419d to your computer and use it in GitHub Desktop.
# Here is an example of some syntax I'm proposing: | |
# See the github repo at https://github.com/mikeckennedy/python-switch | |
def test_switch(): | |
num = 7 | |
val = input("Enter a key. a, b, c or any other: ") | |
with Switch(val) as s: | |
s.case('a', process_a) | |
s.case('b', process_b) | |
s.case('c', lambda: process_with_data(val, num, 'other values still')) | |
s.default(process_any) | |
# process_a, process_b, and process_any are simple void/void methods | |
def process_a(): | |
print("Found A!") | |
def process_b(): | |
print("Found B!") | |
def process_any(): | |
print("Found Default!") | |
def process_with_data(*value): | |
print("Found with data: {}".format(value)) | |
# Here is a first pass implementation at adding switch | |
class Switch: | |
def __init__(self, value): | |
self.value = value | |
self.cases = {} | |
def default(self, func: Callable[[], None]): | |
self.case('__default__', func) | |
def case(self, key, func: Callable[[], None]): | |
if key in self.cases: | |
raise ValueError("Duplicate case: {}".format(key)) | |
if not func: | |
raise ValueError("Action for case cannot be None.") | |
self.cases[key] = func | |
def __enter__(self): | |
return self | |
def __exit__(self, exc_type, exc_val, exc_tb): | |
func = self.cases.get(self.value) | |
if not func: | |
func = self.cases.get('__default__') | |
if not func: | |
raise Exception("Value does not match any case and there is no default case: value {}".format(self.value)) | |
func() |
I tend to agree with @marksweiss... I also think it's easier when new to Python to grasp "there is no switch, use if/elif instead" than to have to embrace a flavor of switch that uses only functions and the necessary boilerplate around each switch use.
Hi guys, thanks for the kind words and thoughts on this. Have a look here and let me know if you still feel that way:
https://github.com/mikeckennedy/python-switch#why-not-just-raw-dict
Not sure where to post this thought ...
You know, switch/cases and singledispatch
have something in common. The first can say "execute this function for a given value." The second says "execute this function for a given type".
It seems you could adapt your code to dispatch for certain types as well, e.g.:
with Switch(val) as s:
s.case([int, float], process_a)
s.case(list, process_b)
Thanks for this contrib.
First, big fan of the show! Now that we got that out of the way I think the approach of only passing
callables
somewhat defeats the purpose of the main use case for switch in C, C++ and Ruby (and, really, C, where this originated), which is to have all the branches present in short block scopes right there in the switch statement, below each switch condition.There are some semantics that are implicit but strong enough for even moderately experienced to sense when
switch
makes sense. Basically, I would argue, the coreswitch
use case is:The first bullet means this can be a better choice (for clarity) than
if/else
, because the conditions are more clearly related and more concisely written. OTOH if you have more complex branching tests you needif/else
. The second bullet, I'm arguing, is the raison d'etre of this construct.I like what you are trying to do, but right now it's too close to syntactic sugar over branching + function calls.