如何发布python函数的参数列表?

时间:2014-11-23 20:23:55

标签: python optional-arguments keyword-argument

我试图找到一种优雅的pythonic方法来解决以下问题。我有一个类VideoAnalysisWidget,它创建了一大堆子窗口小部件,例如VideoFileChoiceWidgetRegionWidget等。我希望VideoAnalysisWidget.__init__将任何适当的关键字参数传递给{ {1}},VideoFileChoiceWidget.__init__,...为了实现这一目标,我想使用以下代码:

RegionWidget.__init__

然后,在其他地方:

import inspect

def filterArgs(func, **kwargs):
    spec = inspect.getargspec(func)
    ks = set(kwargs.keys())
    ks.discard('self')
    kinc = ks & set(spec.args)
    kexc = ks - set(spec.args)
    inc = {k: kwargs[k] for k in kinc}
    exc = {k: kwargs[k] for k in kexc}
    return(inc, exc)

以这种方式做事,def __init__(self, **kwargs): (fcwArgs, otherArgs) = filterArgs(VideoFileChoiceWidget.__init__, **kwargs) fcw = VideoFileChoiceWidget(**fcwArgs) ... 的代码不必跟踪VideoAnalysisWidget的参数列表。如果我稍后修改VideoFileChoiceWidget以采取新的论点,我就不需要在远程VideoFileChoiceWidget代码中进行更改。

现在问题出在这里。如果VideoAnalysisWidget仅明确定义了关键字参数,则此方法可以正常工作。 (顺便说一下,我没有使用VideoFileChoiceWidget以外的任何位置参数来处理这些函数。)但如果self也有VideoFileChoiceWidget参数怎么办?事实上,它确实是因为它是**kwargs的子类,所以我想传递任何额外的参数。 (而且ContainerWidget仅取ContainerWidget。)从根本上说,问题是我的这个解决方案无法嵌套。

一种可能的解决方案是将附加参数列表附加到**kwargs,例如:

VideoFileChoiceWidget.__init__

...然后修改VideoFileChoiceWidget.__init__._args = ['description', 'visible', 'children'] 以使用此选项(如果可用)。但有没有更好,更pythonic的方式呢?

1 个答案:

答案 0 :(得分:0)

我正在使用我原始帖子中概述的方法的变体,其中我将属性附加到__init__。但是,我采用更灵活的方法来使用函数,而不是将其列为列表。我定义了一个未附加的函数argNames,它使用一个或多个类或函数作为参数,并使用inspect返回一组参数名称,并递归地调用函数的argNames属性。然后我可以使用:

将其附加到VideoFileChoiceWidget.__init__(例如)
class VideoFileChoiceWidget(ContainerWidget):
    ...<stuff>...
    def __init__(arg1, arg2, ..., **kwargs):
        ...<more stuff>...

    __init__.argNames = lambda: (argNames(ContainerWidget) | 
                                 {'description', 'visible', 'children'})

任何想要将参数传递给VideoFileChoiceWidget的人都会使用问题中所述的filterArgs。我已经实现了它,它似乎工作。

以下是argNamesfilterArgs的当前定义供参考。

def argNames(*args):
    """
    Get the names of the argument for a function

    Returns:
    --------
    A set whose members are the argument names

    Parameters:
    -----------
    func1, func2, ... -- the functions to be analyzed. The arguments
        are discovered by inspecting func. In addition, if
        func.argNames is defined, it is called (without arguments) to
        get a list of addition names. Any number of functions may be
        listed. If func is a class, func.__init__ is used
        instead. 'self' is always removed from the argument list.
    """
    names = set({})
    for a in args:
        try:
            func = a
            spec = inspect.getargspec(func)
        except TypeError:
            func = a.__init__
            spec = inspect.getargspec(func)
        names |= set(spec.args)
        try:
            names |= set(func.argNames())
        except AttributeError:
            pass
    names.discard('self')
    return(names)

def filterArgs(func, **kwargs):
    """
    Select keyword arguments for a function
    """
    args = argNames(func)
    ks = set(kwargs.keys())
    ks.discard('self')
    kinc = ks & args
    kexc = ks - args
    inc = {k: kwargs[k] for k in kinc}
    exc = {k: kwargs[k] for k in kexc}
    return(inc, exc)