简体   繁体   English

从函数集合中获取各自的* args,** wargs,然后使用提供的1d参数填充不规则的2d结构

[英]Getting respective *args, **kwargs from collection of functions, then filling in the irregular 2d structure with provided 1d arguments

I have data like this: 我有这样的数据:

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

And I have functions, inside an object, which want these data as arguments. 我在对象内部有一些函数,需要这些数据作为参数。 They are to be provided via a method which has this sort of signature (represented just as *args ): 它们将通过具有这种签名的方法(以*args表示)来提供:

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

Or, more explicitly in its structure: 或者,更明确地说是其结构:

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

(I hope it's clear enough what I'm expecting in this method.) (我希望很清楚我对这种方法的期望。)

Let's say, for the sake of example, that my two functions are as follows: 举例来说,我的两个功能如下:

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

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

funcs = func1, func2    # for iteration

With that background, here are the two problems I'm having. 在这种背景下,这是我遇到的两个问题。

Getting the format of *args , **kwargs for each function 获取每个函数的*args**kwargs格式

I have been trying to use the inspect module to get the argument spec for each function, so that I may 'fill in' the structure with the 1d data in args and kwargs . 我一直在尝试使用inspect模块来获取每个函数的参数规范,以便我可以用argskwargs的1d数据“填充”结构。 I've tried the following: 我尝试了以下方法:

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

But I don't understand why the spec.keywords always gives me None (seems kind of asinine). 但是我不明白为什么spec.keywords总是给我None (似乎有点愚蠢)。 The default value of any keyword argument will appear in spec.defaults , but I don't know how to associate it with the correct keyword argument. 任何关键字参数的默认值将出现在spec.defaults ,但我不知道如何将其与正确的关键字参数关联。 (It's also frustrating that all of the keyword arguments are put into spec.args .) (同样令人沮丧的是,所有关键字参数都放入spec.args 。)

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

Filling in the structure to be passed to callFunctions 填写要传递给callFunctions的结构

Assuming I had the structure anyway, filling that structure in with the original data is tricky: Basically, I want to fill the first tuple of *args, **kwargs with the first however-many positional arguments, and the appropriate keyword arguments; 假设我仍然拥有该结构,那么用原始数据填充该结构是很棘手的:基本上,我想用第一个但是很多的位置参数和适当的关键字参数填充*args, **kwargs的第一个元组。 then, move on to the next tuple with the next however-many positional arguments and appropriate keyword arguments; 然后,使用下一个但是很多的位置参数和适当的关键字参数进入下一个元组; and so on. 等等。 I've tried the following: 我尝试了以下方法:

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

But I get this: 但是我得到这个:

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

Why don't I get baz ? 为什么我没有得到baz

For the first part of your question: The thing is that (in Python 2) there isn't a complete distinction between "keyword" and "positional" arguments as such. 对于问题的第一部分:问题是(在Python 2中)“关键字”和“位置”参数之间并没有完全区别。 Any argument value can be passed positionally or by keyword. 任何参数值都可以按位置或通过关键字传递。 It's just that if the argument doesn't have a default value, that argument must be supplied --- but it can still be passed by keyword. 只是,如果参数没有默认值,则必须提供该参数---但仍可以通过关键字传递它。 (In Python 3 it's possible to have true keyword-only arguments that cannot be passed positionally.) (在Python 3中,可能有无法在位置上传递的纯关键字纯参数。)

The way to match up the arguments with their defaults, then, is to realize that the defaults can only apply to the last arguments. 然后,将参数与其默认值匹配的方法是认识到默认值只能应用于最后一个参数。 So if a function accepts four arguments and has two default argument values, those values must be for the third and fourth arguments, with the first two arguments having no default values. 因此,如果一个函数接受四个参数并具有两个默认参数值,则这些值必须用于第三个和第四个参数,而前两个参数没有默认值。 In common parlance, this is a function with two positional arguments and two keyword arguments (although, as I mentioned, this is only half-accurate, since any argument can always be passed by keyword). 通常来说,这是一个具有两个位置参数和两个关键字参数的函数(尽管正如我所提到的,这只是半精度的,因为任何参数都可以始终通过关键字传递)。 As the documentation says, the keywords part of the result of getargspec is not meant to hold the keyword arguments; 文档所述, getargspec结果的keywords部分并不意味着保留关键字参数。 it holds the name of the keyword varargs argument (eg, **kwargs ), if there is one. 它包含关键字varargs参数的名称(例如**kwargs )(如果有的话)。

To see how to match them up, just look at the source code for inspect.formatargspec , which does essentially this (I changed it slightly to build a list instead of a string representation): 要查看如何将它们匹配,只需查看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,))

For the second part of your question, the problem is just that you're slicing from positional_index:however_many instead of positional_index:positional_index+however_many . 对于问题的第二部分,问题在于您是从positional_index:however_many切片,而不是positional_index:positional_index+however_many Also, your dict/set confusion is because you're using a set comprehension instead of a dict comprehension. 另外,您的dict / set困惑是因为您使用的是集合理解而不是dict理解。 Plus, you don't need that call to tuple . 另外,您不需要对该tuple调用。 Do it like this: 像这样做:

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

Note, though, that one problem with this is you'll be unable to pass different-valued keyword arguments of the same name to different functions. 但是请注意,与此有关的一个问题是,您将无法将同名的不同值的关键字参数传递给不同的函数。 If you have def foo(a, b, c=2) and def bar(a, b, c=8) , you have no way to pass {'c': 1} to foo and {'c': 2} to bar , because you're only passing one dict at the beginning, which can only have one 'c' key. 如果您具有def foo(a, b, c=2)def bar(a, b, c=8) ,则无法将{'c': 1}传递给foo{'c': 2}bar ,因为您在一开始只能传递一个字典,该字典只能有一个'c'键。

More generally, if you really want to handle any legal argument set, it won't be as simple as this, because even "positional" arguments can be passed by keyword. 更笼统地说,如果您真的想处理任何合法的参数集,它就不会这么简单,因为即使“位置”参数也可以通过关键字传递。 If you have a function like def foo(a, b, c=3, d=4) , it's legal to call it with foo(1, d=8, c=7, b=6) --- you can pass the keyword arguments out of order, and also pass a value for b by keyword, even though it has no default value. 如果您具有def foo(a, b, c=3, d=4)之类的def foo(a, b, c=3, d=4) ,则使用foo(1, d=8, c=7, b=6)调用它是合法的---您可以传递关键字参数乱序,并且即使没有默认值,也按关键字传递b的值。 So you can't in general just grab positional arguments based on the number of arguments without default values; 因此,通常不能仅基于没有默认值的参数数量来获取位置参数。 you have to actually look at which particular arguments were passed by keyword, and only positionally pass the ones that weren't passed by keyword. 您必须实际查看哪些特定参数通过关键字传递,而仅在位置上传递那些未通过关键字传递的参数。 (You could of course just say that your function won't work for this sort of case; it depends how general you want it to be.) (您当然可以说您的函数在这种情况下不起作用;这取决于您希望它的通用性。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM