{"id":13468672,"url":"https://github.com/mikeckennedy/python-switch","last_synced_at":"2025-04-05T14:09:57.960Z","repository":{"id":57472779,"uuid":"103439774","full_name":"mikeckennedy/python-switch","owner":"mikeckennedy","description":"Adds switch blocks to Python #pypackage","archived":false,"fork":false,"pushed_at":"2023-01-08T08:17:19.000Z","size":42,"stargazers_count":248,"open_issues_count":0,"forks_count":21,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-29T13:09:29.680Z","etag":null,"topics":["language","python","python3","switch"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mikeckennedy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-13T19:06:54.000Z","updated_at":"2025-02-04T12:21:20.000Z","dependencies_parsed_at":"2023-02-08T05:15:29.522Z","dependency_job_id":null,"html_url":"https://github.com/mikeckennedy/python-switch","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeckennedy%2Fpython-switch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeckennedy%2Fpython-switch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeckennedy%2Fpython-switch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikeckennedy%2Fpython-switch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mikeckennedy","download_url":"https://codeload.github.com/mikeckennedy/python-switch/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247345856,"owners_count":20924102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["language","python","python3","switch"],"created_at":"2024-07-31T15:01:16.438Z","updated_at":"2025-04-05T14:09:57.938Z","avatar_url":"https://github.com/mikeckennedy.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# switchlang\n[![](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/) \n[![](https://img.shields.io/pypi/l/markdown-subtemplate.svg)](https://github.com/mikeckennedy/python-switch/blob/master/LICENSE)\n[![](https://img.shields.io/pypi/dm/switchlang.svg)](https://pypi.org/project/switchlang/)\n\n\nAdds switch blocks to the Python language.\n\nThis module adds explicit switch functionality to Python \nwithout changing the language. It builds upon a standard\nway to define execution blocks: the `with` statement.\n\n## Example\n\n```python\nfrom switchlang import switch\n\ndef main():\n    num = 7\n    val = input(\"Enter a character, a, b, c or any other: \")\n\n    with switch(val) as s:\n        s.case('a', process_a)\n        s.case('b', lambda: process_with_data(val, num, 'other values still'))\n        s.default(process_any)\n    \ndef process_a():\n    print(\"Found A!\")\n    \ndef process_any():\n    print(\"Found Default!\")\n    \ndef process_with_data(*value):\n    print(\"Found with data: {}\".format(value))\n\nmain()\n``` \n\n## Installation\n\nSimply install via pip:\n\n```bash\npip install switchlang\n```\n\n## Features\n\n* More explicit than using dictionaries with functions as values.\n* Verifies the signatures of the methods\n* Supports default case\n* Checks for duplicate keys / cases\n* Keys can be anything hashable (numbers, strings, objects, etc.)\n* Could be extended for \"fall-through\" cases (doesn't yet)\n* Use range and list for multiple cases mapped to a single action\n\n## Multiple cases, one action\n\nYou can map ranges and lists of cases to a single action as follows:\n\n```python\n# with lists:\nvalue = 4  # matches even number case\n\nwith switch(value) as s:\n    s.case([1, 3, 5, 7], lambda: ...)\n    s.case([0, 2, 4, 6, 8], lambda: ...)\n    s.default(lambda: ...)\n```\n\n```python\n# with ranges:\nvalue = 4  # matches first case\n\nwith switch(value) as s:\n    s.case(range(1, 6), lambda: ...)\n    s.case(range(6, 10), lambda: ...)\n    s.default(lambda: ...)\n```\n\n## Closed vs. Open ranges\n\nLooking at the above code it's a bit weird that 6 appears \nat the end of one case, beginning of the next. But `range()` is\nhalf open/closed. \n\nTo handle the inclusive case, I've added `closed_range(start, stop)`.\nFor example, `closed_range(1,5)` -\u003e `[1,2,3,4,5]` \n\n## Why not just raw `dict`?\n\nThe biggest push back on this idea is that we already have this problem solved.\nYou write the following code.\n\n```python\nswitch = {\n    1: method_on_one,\n    2: method_on_two,\n    3: method_three\n}\n\nresult = switch.get(value, default_method_to_run)()\n```\n\nThis works but is very low on the functionality level. We have a better solution here \nI believe. Let's take this example and see how it looks in python-switch vs raw dicts:\n\n```python\n# with python-switch:\n\nwhile True:\n    action = get_action(action)\n\n    with switch(action) as s:\n        s.case(['c', 'a'], create_account)\n        s.case('l', log_into_account)\n        s.case('r', register_cage)\n        s.case('u', update_availability)\n        s.case(['v', 'b'], view_bookings)\n        s.case('x', exit_app)\n        s.case('', lambda: None)\n        s.case(range(1,6), lambda: set_level(action))\n        s.default(unknown_command)\n    \n    print('Result is {}'.format(s.result))\n```\n\nNow compare that to the espoused *pythonic* way:\n\n```python\n# with raw dicts\n\nwhile True:\n    action = get_action(action)\n\n    switch = {\n        'c': create_account,\n        'a': create_account,\n        'l': log_into_account,\n        'r': register_cage,\n        'u': update_availability,\n        'v': view_bookings,\n        'b': view_bookings,\n        'x': exit_app,\n        1: lambda: set_level(action),\n        2: lambda: set_level(action),\n        3: lambda: set_level(action),\n        4: lambda: set_level(action),\n        5: lambda: set_level(action),\n        '': lambda: None,\n    }\n    result = switch.get(action, unknown_command)()\n    print('Result is {}'.format(result))\n```\n\nPersonally, I much prefer to read and write the one above. That's why I wrote this module.\nIt seems to convey the intent of switch way more than the dict. But either are options.\n\n## Why not just `if / elif / else`?\n\nThe another push back on this idea is that we already have this problem solved.\nSwitch statements are really if / elif / else blocks. So you write the following code.\n\n```python\n# with if / elif / else\n\nwhile True:\n    action = get_action(action)\n\n    if action == 'c' or action == 'a':\n        result = create_account()\n    elif action == 'l':\n        result = log_into_account()\n    elif action == 'r':\n        result = register_cage()\n    elif action == 'a':\n        result = update_availability()\n    elif action == 'v' or action == 'b':\n        result = view_bookings()\n    elif action == 'x':\n        result = exit_app()\n    elif action in {1, 2, 3, 4, 5}:\n        result = set_level(action)\n    else:\n        unknown_command()\n        \n    print('Result is {}'.format(result))\n```\n\nI actually believe this is a little better than the \n[raw dict option](https://github.com/mikeckennedy/python-switch#why-not-just-raw-dict).\nBut there are still things that are harder. \n\n* How would you deal with fall-through cleanly?\n* Did you notice the bug? We forgot to set result in default case (`else`) and will result in a runtime error (but only if that case hits).\n* There is another bug. Update `update_availability` will never run because it's command (`a`) is bound to two cases. \nThis is guarded against in switch and you would receive a duplicate case error the first time it runs at all.\n* While it's pretty clear, it's much more verbose and less declarative than the switch version. \n\nAgain, compare the if / elif / else to what you have with switch. This code is identical except \ndoesn't have the default case bug.\n\n```python\nwhile True:\n    action = get_action(action)\n\n    with switch(action) as s:\n        s.case(['c', 'a'], create_account)\n        s.case('l', log_into_account)\n        s.case('r', register_cage)\n        s.case('u', update_availability)\n        s.case(['v', 'b'], view_bookings)\n        s.case('x', exit_app)\n        s.case('', lambda: None)\n        s.case(range(1,6), lambda: set_level(action))\n        s.default(unknown_command)\n    \n    print('Result is {}'.format(s.result))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikeckennedy%2Fpython-switch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmikeckennedy%2Fpython-switch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikeckennedy%2Fpython-switch/lists"}