TL; TR寻找成语和模式来解压缩位置和关键字参数到有序的位置参数序列,基于简单的说明书中,例如名单列表。这个想法似乎相似,类似scanf解析。灵活处理函数参数在Python
我包装一个Python模块的功能,称为someapi
。的someapi
功能只期望位置参数,这是在疼痛数字在大多数情况下。 我想能够与他们如何将参数传递给我的包装的灵活性来电。 下面是我想使包装的调用示例:
# foo calls someapi.foo()
foo(1, 2, 3, 4)
foo(1, 2, 3, 4, 5) # but forward only 1st 4 to someapi.foo
foo([1, 2, 3, 4])
foo([1, 2, 3, 4, 5, 6]) # but forward only 1st 4 to someapi.foo
foo({'x':1, 'y':2, 'z':3, 'r':4})
foo(x=1, y=2, z=3, r=4)
foo(a=0, b=0, x=1, y=2, z=3, r=4) # but forward only x,y,z,r someapi.foo
我看不出有任何需要支持的混合位置和关键字参数错综复杂的情况下:
foo(3, 4, x=1, y=2)
这里是我的在实施这样的论点处理为foo
包装调用someapi.foo
第一个尝试:
def foo(*args, **kwargs):
# BEGIN arguments un/re-packing
a = None
kwa = None
if len(args) > 1:
# foo(1, 2, 3, 4)
a = args
elif len(args) == 1:
if isinstance(args[0], (list, tuple)) and len(args[0]) > 1:
# foo([1, 2, 3, 4])
a = args[0]
if isinstance(args[0], dict):
# foo({'x':1, 'y':2, 'z':3, 'r':4})
kwa = args[0]
else:
# foo(x=1, y=2, z=3, r=4)
kwa = kwargs
if a:
(x, y, z, r) = a
elif kwa:
(x, y, z, r) = (kwa['x'], kwa['y'], kwa['z'], kwa['r'])
else:
raise ValueError("invalid arguments")
# END arguments un/re-packing
# make call forwarding unpacked arguments
someapi.foo(x, y, z, r)
它的工作为e xpected,据我所知道的,但它有两个问题:
- 我可以做更多的Python的成语方式更好?
- 我有12打
someapi
函数来包装,所以如何避免复制和调整整个块之间的BEGIN/END标记在每个包装?
我不知道这个问题1的答案,但。
然而,这里是我试图解决2
所以,我定义的基础上,names
简单的规格参数的通用处理的问题。 的names
指定几件事情,这取决于实际的包装调用:
- 多少个参数从
*args
解压? (见下文len(names)
测试) - 预计
**kwargs
什么关键字参数?(见下文generator expression返回数组)
这里是新版本:
def unpack_args(names, *args, **kwargs):
a = None
kwa = None
if len(args) >= len(names):
# foo(1, 2, 3, 4...)
a = args
elif len(args) == 1:
if isinstance(args[0], (list, tuple)) and len(args[0]) >= len(names):
# foo([1, 2, 3, 4...])
a = args[0]
if isinstance(args[0], dict):
# foo({'x':1, 'y':2, 'z':3, 'r':4...})
kwa = args[0]
else:
# foo(x=1, y=2, z=3, r=4)
kwa = kwargs
if a:
return a
elif kwa:
if all(name in kwa.keys() for name in names):
return (kwa[n] for n in names)
else:
raise ValueError("missing keys:", \
[name for name in names if name not in kwa.keys()])
else:
raise ValueError("invalid arguments")
这让我实现以下面的方式包装功能:
def bar(*args, **kwargs):
# arguments un/re-packing according to given of names
zargs = unpack_args(('a', 'b', 'c', 'd', 'e', 'f'), *args, **kwargs)
# make call forwarding unpacked arguments
someapi.bar(*zargs)
我觉得我已经实现了与我正在寻找的foo
版本相比的所有优点:
以请求的灵活性启用呼叫者。
紧凑的形式,减少了复制和粘贴。
灵活的位置参数协议:
bar
可以用7,8个以上的位置参数或一长串数字来调用,但只考虑前6个参数。例如,它将允许迭代处理数字的长列表(例如认为几何坐标):
# meaw expects 2 numbers
n = [1,2,3,4,5,6,7,8]
for i in range(0, len(n), 2):
meaw(n[i:i+2])
- 灵活的协议为关键字参数:多个关键字可能超过指定实际使用或字典可以有更多的项目比使用。
回到上面的问题1,我可以做得更好吗?让它变得更加Pythonic?
此外,我想问我的解决方案的审查:你看到任何错误?我忽视了什么?如何改善它?
这是一个奇怪的想要的行为。作为调用者,如果我用'foo(1,2,3,4,5)'调用一个函数'我会惊讶地发现我的一个论点被忽略了。例如,我不会想到调用'foo(1,2,3)'并且有第4个默认参数,但删除参数很奇怪。你为什么期望这个API的用户使用不正确的参数数目调用函数? – CoryKramer 2014-12-03 12:39:40
@Cyber我理解你的推理。两件事:1)这样的扩展协议是次要优势2),但如果可用的话,它有用例。我用'meaw'函数添加了一个例子来说明一个这样的用例。所以,这不是关于无视论证,而是我更多地将其视为隐式切片。 Ceratinly,它必须记录下来,以便我的包装的用户知道这种功能。 – mloskot 2014-12-03 13:14:16
如果用户添加位置和关键字参数,会发生什么情况? “无效论据”我猜? – KurzedMetal 2014-12-03 13:25:01