優美勝於醜陋
import this
博客地址: Specific-Dispatch
表驅動法是一種編輯模式(Scheme)——從表裏面查找信息而不使用邏輯語句(if 和 case)。事實上,凡是能經過邏輯語句來選擇的事物,均可以經過查表來選擇。html
對簡單的狀況而言,使用邏輯語句更爲容易和直白。但隨着邏輯鏈的愈來愈複雜,查表法也就愈發顯得更具吸引力。python
因爲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