【问题标题】:How to promulgate a list of arguments for a python function?如何发布 python 函数的参数列表?
【发布时间】:2015-01-21 12:25:19
【问题描述】:

我正在尝试寻找一种优雅的 Python 方式来解决以下问题。我有一个类VideoAnalysisWidget,它创建了一大堆子小部件,例如VideoFileChoiceWidgetRegionWidget 等。我希望VideoAnalysisWidget.__init__ 将任何适当的关键字参数传递给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']

...然后修改filterArgs 以使用它(如果可用)。但是有没有更好、更 Pythonic 的方式来做到这一点?

【问题讨论】:

  • 我不确定这是否是最好的方法,因为使用了inspect,但在这些情况下,我通常将大量 kwarg 分组到一个类中并传递它。
  • 是的,我认为这是一种解决方案。反对它的论点(这可能不是一个好的论点)是它本质上重建了用户代码中内置的 KW arg 传递函数,使一切变得不那么透明。
  • 这是我的看法:当参数共享并且对于子类来说太大而无法管理时,正如您在您的案例中所说的那样,不妨这样做以分离关注点。它实际上经常发生。

标签: python optional-arguments keyword-argument


【解决方案1】:

我将继续使用我在原始帖子中概述的方法的变体,其中我将属性附加到__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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    • 2012-08-06
    • 1970-01-01
    • 2014-01-22
    • 2011-02-22
    相关资源
    最近更新 更多