优美胜于丑陋
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