写一个命令分发器 初步要求 :
程序员可以方便地注册函数到某一个命令, 用户输入命令时, 路由到注册的函数, 如果此命令没有对应的函数, 执行默认函数 分析 :
输入命令映射到一个函数, 并执行这个函数, 应该是cmd_tbl[cmd] = fn 的形式, 使用字典比较合适 如果输入某一cmd 命令后, 没有找到函数就调用缺省的函数执行, 正好是字典的缺省参数, cmd 是字符串 基础框架 :
cmd_table = {}
def reg(cmd, fn): cmd_table[cmd] = fn
def default_fun(): print('Unknow command')
def dispatcher(): while True: cmd =input('>>')
if cmd.strip() == '':
return
cmd_table.get(cmd, default_fun)()
def foo1(): print('text') def foo2(): print('python')
reg('x', foo1) reg('pyt', foo2)
dispatcher() 存在问题 :
函数注册太难看 函数和字典都在全局定义 如何在此基础上改进…
封装 将reg函数封装成装饰器, 并用它来注册函数
def reg(cmd): def _reg(fn): cmd_table[cmd] = fn return fn return _reg
@reg('x') def foo1(): print('text') br/>@reg('pyt') def foo2(): print('python') 能否把字典, reg, dispatcher等也封装起来呢, 外面在使用的时候调度就可以了
def command_dispatcher():
cmd_table = {}
# 注册函数
def reg(cmd):
def _reg(fn):
cmd_table[cmd] = fn
return fn
return _reg
# 缺省函数
def default_func():
print('Unknow command')
# 分发器, 调度
def dispatcher():
while True:
cmd = input('>>')
# 退出条件
if cmd.strip() == '':
return
cmd_table.get(cmd, default_func)()
return reg, dispatcher
# 返回内层函数, 解构后外部调用
reg, dispatcher = command_dispatcher()
@reg('x') def foo1():br/>print('text') @reg('pyt') def foo2(): print('python')
dispatcher() 输出结果 :
abcdef Unknow command x text pyt python 符合设计预期 : 输入的cmd不存在的调用缺省函数, cmd存在则调用相应函数, 输入空格则退出 问题 重复注册
如果一个函数使用同样的cmd名注册, 就等于覆盖原有cmd到fn之间的关系, 这样的判断也是合理的, 不过也可以加一个判断, 如果key 已存在, 重复注册抛出异常, 具体情况具体分析 注销
有注册就应该有注销, 从字典中移除. 一般来说注销是有条件的, 什么人拥有注销权限看业务需求 完善命令分发器 实现函数可以带任意参数(可变参数除外), 解析参数并要求用户输入 即实现下面的问题 :
@reg('x') def foo1(x, y): print('text', x, y)br/>@reg('pyt') def foo2(a, b=100): print('python', a, b) 基本思路 :
可以有以下两种方式 : 注册的时候固定死, @reg(‘pyt’, 200, 100), 可以认为@reg(‘pyt’, 200, 100) 和 @reg(‘pyt’, 300, 100) 是不同的函数, 可以用partial 函数 运行时,在输入cmd 的时候, 逗号或者空格分割, 获取参数, 函数验证功能在后面实现 一般用户都喜欢使用单纯一个命令如 pyt, 然后直接显示想要的结果,采用方式一实现
def command_dispatcher():
# 构建全局字典
cmd_tbl = {}
# 注册函数
def reg(cmd, *args, **kwargs):
def _reg(fn):
cmd_tbl[cmd] = fn, args, kwargs
return fn
return _reg
# 缺省函数
def default_func(*args, **kwargs):
print('Unknow command')
# 调度器
def dispatcher():
while True:
cmd = input('Please input cmd >>')
# 退出条件
if cmd.strip() == '':
return
fn, args, kwargs = cmd_tbl.get(cmd, (default_func, (), {}))
fn(*args, kwargs)
return reg, dispatcher
reg, dispatcher = command_dispatcher()
@reg('x1', z=200, y=300, x=100) @reg('x2', z=300, y=300, x=300) @reg('x3', 1, 2, 3) def foo1(x, y, z): print('text', x, y, z)
dispatcher() 输出结果 :
Please input cmd >> x1 text 100 300 200 Please input cmd >> x2 text 300 300 300 Please input cmd >> x3 text 1 2 3 Please input cmd >> 方法二 :
def command_dispatcher(): command = {} def reg(cmd): def _reg(fn): command[cmd] = fn return fn return _reg def default_fn(*args, kwargs): print('Unknow command') def dispatcher(): while True: cmd = input('Please input cmd >>') if cmd.strip() == '': break else: fname, params = cmd.replace(',', ' ').split() args = [] kwargs = {} for param in params: lis_param = param.split('=', maxsplit=1) if len(lis_param) == 1: args.append(lis_param[0]) elif len(lis_param) == 2: kwargs[lis_param[0]] = lis_param[1] command.get(fname, default_fn)(args, kwargs) return reg, dispatcher reg, dispatcher = command_dispatcher()
@reg('x') def foo1(x, y): print('text', x, y)
@reg('pyt') def foo2(a, b=100): print('python', a, b)
dispatcher()
输出结果 :
Please input cmd >> x 1 2 text 1 2 Please input cmd >> x x=1,y=4 text 1 4 Please input cmd >> pyt 5 python 5 100 Please input cmd >> pyt 4 8 python 4 8