0

我有这样的数据:

args, kwargs = (('foo', 'bar', 'baz'), {'goo': 1})

我在对象内部有函数,它们希望这些数据作为参数。它们将通过具有这种签名的方法提供(表示为*args):

callFunctions((*args, **kwargs), ...)

或者,在其结构中更明确:

callFunctions(((args ...), {kwargs ...}), ...)

(我希望我对这种方法的期望足够清楚。)

假设,为了举例,我的两个函数如下:

def func1(foo, bar):
    print foo, bar

def func2(baz, goo=0):
    print baz, goo

funcs = func1, func2    # for iteration

在这样的背景下,这是我遇到的两个问题。

为每个函数获取*args,的格式**kwargs

我一直在尝试使用该inspect模块来获取每个函数的参数规范,以便我可以用 和 中的一维数据“填充”args结构kwargs。我尝试了以下方法:

format = [(spec.args, spec.keywords) for spec in (inspect.getargspec(func) for func in funcs)]

但我不明白为什么spec.keywords总是给我None(似乎有点愚蠢)。任何关键字参数的默认值都会出现在 中spec.defaults,但我不知道如何将它与正确的关键字参数相关联。(令人沮丧的是,所有关键字参数都放入spec.args.)

[(['foo', 'bar'], None), (['baz', 'goo'], None)]

填写要传递给的结构callFunctions

假设无论如何我都有这个结构,用原始数据填充那个结构是很棘手的:基本上,我想*args, **kwargs用第一个但是很多位置参数和适当的关键字参数填充第一个元组;然后,继续使用下一个包含多个位置参数和适当关键字参数的下一个元组;等等。我尝试了以下方法:

argues = []
positional_index = 0
format = ((('foo', 'bar'), {}), (('baz',), {'goo': 0}))
for pair in format:
    however_many = len(pair[0])
    argues.append((tuple(args[positional_index:however_many]), dict({(k, kwargs[k]) for k in pair[1]})))
    positional_index += however_many

但我明白了:

[(('foo', 'bar'), {}), ((), {'goo': 1})]

为什么我得不到baz

4

1 回答 1

1

对于您问题的第一部分:问题是(在 Python 2 中)“关键字”和“位置”参数之间没有完全区别。任何参数值都可以按位置或关键字传递。只是如果参数没有默认值,则必须提供该参数 --- 但它仍然可以通过关键字传递。(在 Python 3 中,可能有不能按位置传递的真正的仅关键字参数。)

那么,将参数与其默认值匹配的方法是认识到默认值只能应用于最后一个参数。因此,如果一个函数接受四个参数并具有两个默认参数值,则这些值必须用于第三个和第四个参数,前两个参数没有默认值。通俗地说,这是一个具有两个位置参数和两个关键字参数的函数(尽管正如我所提到的,这只是一半准确,因为任何参数都可以通过关键字传递)。正如文档所说,keywords结果的一部分getargspec并不意味着包含关键字参数;它保存关键字可变参数参数的名称(例如,**kwargs),如果有的话。

要了解如何匹配它们,只需查看 的源代码inspect.formatargspec,它本质上就是这样做的(我稍微更改了它以构建一个列表而不是字符串表示):

args, varargs, varkw, defaults = inspect.getargspec(func)
result = []
if defaults:
    firstdefault = len(args) - len(defaults)
for i, arg in enumerate(args):
    if defaults and i >= firstdefault:
        result.append((arg, defaults[i - firstdefault]))
    else:
        result.append((arg,))

对于您问题的第二部分,问题只是您正在切片positional_index:however_many而不是positional_index:positional_index+however_many. 此外,您的 dict/set 混淆是因为您使用的是集合理解而不是 dict 理解。另外,您不需要调用tuple. 像这样做:

for pair in format:
    however_many = len(pair[0])
    argues.append((args[positional_index:positional_index+however_many], {k: kwargs[k] for k in pair[1]}))
    positional_index += however_many

但是请注意,这样做的一个问题是您将无法将相同名称的不同值关键字参数传递给不同的函数。如果你有def foo(a, b, c=2)and def bar(a, b, c=8),你就无法传递{'c': 1}tofoo{'c': 2}to bar,因为你一开始只传递一个 dict ,它只能有一个'c'键。

更一般地说,如果你真的想处理任何合法的参数集,它不会像这样简单,因为即使是“位置”参数也可以通过关键字传递。如果你有一个类似的函数def foo(a, b, c=3, d=4),用 --- 调用它是合法的,foo(1, d=8, c=7, b=6)你可以乱序传递关键字参数,也可以通过关键字传递一个值b,即使它没有默认值。因此,您通常不能仅根据没有默认值的参数数量来获取位置参数;您必须实际查看哪些特定参数是通过关键字传递的,并且只在位置上传递那些没有通过关键字传递的参数。(你当然可以说你的函数不适用于这种情况;这取决于你希望它有多通用。)

于 2013-07-10T17:13:47.757 回答