Python Switch Case 最佳實踐

優美勝於醜陋
import this
博客地址: Specific-Dispatch

前言

表驅動法是一種編輯模式(Scheme)——從表裏面查找信息而不使用邏輯語句(ifcase)。事實上,凡是能經過邏輯語句來選擇的事物,均可以經過查表來選擇。html

對簡單的狀況而言,使用邏輯語句更爲容易和直白。但隨着邏輯鏈的愈來愈複雜,查表法也就愈發顯得更具吸引力。python

Python的switch case

因爲Python中沒有switch case關鍵詞,因此對於每一種狀況的邏輯語句只能用if,elif,else來實現,顯得很不Pythonic.架構

def handle_case(case):
    if case == 1:
        print('case 1')
    elif case == 2:
        print('case 2')
    else:
        print('default case')

而受到PEP-443: Single-dispatch generic functions的啓發,很容易就能實現以下裝飾器:app

from functools import update_wrapper
from types import MappingProxyType
from typing import Hashable, Callable, Union


def specificdispatch(key: Union[int, str] = 0) -> Callable:
    """specific-dispatch generic function decorator.

    Transforms a function into a generic function, which can have different
    behaviours depending upon the value of its key of arguments or key of keyword arguments.
    The decorated function acts as the default implementation, and additional
    implementations can be registered using the register() attribute of the
    generic function.
    """

    def decorate(func: Callable) -> Callable:
        registry = {}

        def dispatch(key: Hashable) -> Callable:
            """
            Runs the dispatch algorithm to return the best available implementation
            for the given *key* registered on *generic_func*.
            """
            try:
                impl = registry[key]
            except KeyError:
                impl = registry[object]
            return impl

        def register(key: Hashable, func: Callable=None) -> Callable:
            """
            Registers a new implementation for the given *key* on a *generic_func*.
            """
            if func is None:
                return lambda f: register(key, f)

            registry[key] = func
            return func

        def wrapper_index(*args, **kw):
            return dispatch(args[key])(*args, **kw)

        def wrapper_keyword(*args, **kw):
            return dispatch(kw[key])(*args, **kw)

        registry[object] = func
        if isinstance(key, int):
            wrapper = wrapper_index
        elif isinstance(key, str):
            wrapper = wrapper_keyword
        else:
            raise KeyError('The key must be int or str')
        wrapper.register = register
        wrapper.dispatch = dispatch
        wrapper.registry = MappingProxyType(registry)
        update_wrapper(wrapper, func)

        return wrapper

    return decorate

而以前的代碼就能很優美的重構成這樣:this

@specificdispatch(key=0)
def handle_case(case):
    print('default case')

@handle_case.register(1)
def _(case):
    print('case 1')

@handle_case.register(2)
def _(case):
    print('case 2')

handle_case(1) # case 1
handle_case(0) # default case

而對於這樣的架構,即易於擴展也利於維護。spa

更多實例

class Test:
    @specificdispatch(key=1)
    def test_dispatch(self, message, *args, **kw):
        print(f'default: {message} args:{args} kw:{kw}')

    @test_dispatch.register('test')
    def _(self, message, *args, **kw):
        print(f'test: {message} args:{args} kw:{kw}')

test = Test()
# default: default args:(1,) kw:{'test': True}
test.test_dispatch('default', 1, test=True)
# test: test args:(1,) kw:{'test': True}
test.test_dispatch('test', 1, test=True)

@specificdispatch(key='case')
def handle_case(case):
    print('default case')

@handle_case.register(1)
def _(case):
    print('case 1')

@handle_case.register(2)
def _(case):
    print('case 2')

handle_case(case=1)  # case 1
handle_case(case=0)  # default case
相關文章
相關標籤/搜索