优美胜于丑陋 import this
博客地址:Specific-Dispatch
表驱动法是一种编辑模式( Scheme )——从表里面查找信息而不使用逻辑语句(if 和 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的启发,很容易就能实现如下装饰器:
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
而之前的代码就能很优美的重构成这样:
@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
而对于这样的架构,即易于扩展也利于维护。
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
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.