函数如何将** kwargs作为其参数并将它们分配到不同的类中?

时间:2017-01-05 02:16:09

标签: python initialization arguments keyword-argument

假设我有两个班级......

class A:

    def __init__(self, *args, arg1="default1", arg2="default2"):
        # Initialise class A


class B:

    def __init__(self, arg3="default3", arg4="default4"):
        # Initialise class B

每个类都有自己的关键字参数,其中一个有位置参数。

现在假设有一个函数可以创建每个类的实例,使用自己的参数来执行此操作:

def make_objs(*args, arg1="default1", arg2="default2", arg3="default3", arg4="default4"):
    objA = ClassA(*args, arg1=arg1, arg2=arg2)
    objB = ClassB(arg3=arg3, arg4=arg4)

这里我手动将函数接收的关键字参数分配给正确的类。这有点单调乏味 - 我必须在函数定义中复制关键字,更改类将意味着更改函数的参数。

理想情况下,我会做这样的事情:

def make_objs(*args, **kwargs):
    objA = ClassA(*args, **kwargs)
    objB = ClassB(**kwargs)

每个类将获取所有关键字参数,并仅提取与其相关的参数。这不是上面代码实际上会做的事情,它会抛出异常,因为ClassA不期望一个名为arg3的参数。

有没有这样做?某种方法使函数将**kwargs作为参数并确定哪些参数可以转到哪个类?

3 个答案:

答案 0 :(得分:1)

为避免所有重复输入,您可以创建一个实用程序函数,该函数使用inspect模块检查所涉及的函数/方法的调用顺序。

这是将代码应用于代码的可运行示例。效用函数是名为get_kwarg_names

的函数
from inspect import signature, Parameter

class ClassA:
    def __init__(self, *args, arg1="default1", arg2="default2"):
        print('ClassA.__init__(): '
              '*args={}, arg1={!r}, arg2={!r}'.format(args, arg1, arg2))

class ClassB:
    def __init__(self, arg3="default3", arg4="default4"):
        print('ClassB.__init__(): arg3={!r}, arg4={!r}'.format(arg3, arg4))

def get_kwarg_names(function):
    """ Return a list of keyword argument names function accepts. """
    sig = signature(function)
    keywords = []
    for param in sig.parameters.values():
        if(param.kind == Parameter.KEYWORD_ONLY or
            (param.kind == Parameter.POSITIONAL_OR_KEYWORD and
                param.default != Parameter.empty)):
            keywords.append(param.name)
    return keywords

# Sample usage of utility function above.
def make_objs(*args, arg1="default1", arg2="default2",
                     arg3="default3", arg4="default4"):

    local_namespace = locals()
    classA_kwargs = {keyword: local_namespace[keyword]
                        for keyword in get_kwarg_names(ClassA.__init__)}
    objA = ClassA(*args, **classA_kwargs)

    classB_kwargs = {keyword: local_namespace[keyword]
                        for keyword in get_kwarg_names(ClassB.__init__)}
    objB = ClassB(**classB_kwargs)

make_objs(1, 2, arg1="val1", arg2="val2", arg4="val4")

输出:

ClassA.__init__(): *args=(1, 2), arg1='val1', arg2='val2'
ClassB.__init__(): arg3='default3', arg4='val4'

答案 1 :(得分:0)

如果您在课程的*arg方法中添加**kwargs__init__,则可以实现您期望的行为。就像下面的例子中一样:

class A(object):

    def __init__(self, a, b, *arg, **kwargs):
        self.a = a
        self.b = b

class B(object):

    def __init__(self, c, d, *arg, **kwargs):
        self.c = c
        self.d = d

def make_objs(**kwargs):
    objA = A(**kwargs)
    objB = B(**kwargs)

make_objs(a='apple', b='ball', c='charlie', d='delta')

但需要注意的是,如果您打印objA.cobjA.d,则会在charlie参数中返回deltamake_objs

答案 2 :(得分:0)

class A:
    def __init__(self, *args, a1 = 'd1', a2 = 'd2'):
        self.a1 = a1
        self.a2 = a2
class B:
    def __init__(self, a3 = 'd3', a4 = 'd4'):
        self.a3 = a3
        self.a4 = a4

使用inpect.signature获取呼叫签名,然后在创建对象之前过滤kwargs

from inspect import signature
from inspect import Parameter

sig_a = signature(A)
sig_b = signature(B)

def f(*args, **kwargs):
    d1 = {}
    d2 = {}
    for k, v in kwargs.items():
        try:
            if sig_a.parameters[k].kind in (Parameter.KEYWORD_ONLY,
                                            Parameter.POSITIONAL_OR_KEYWORD,
                                            Parameter.VAR_KEYWORD):
                 d1[k] = v
        except KeyError:
            pass
        try:
            if sig_b.parameters[k].kind in (Parameter.KEYWORD_ONLY,
                                            Parameter.POSITIONAL_OR_KEYWORD,
                                            Parameter.VAR_KEYWORD):
                d2[k] = v
        except KeyError:
            pass
    return (A(args, **d1), B(**d2))

d = {'a1':1, 'a2':2, 'a3':3, 'a4':4}   
x, y = f(2, **d)

>>> x.a1
1
>>> x.a2
2
>>> y.a3
3
>>> y.a4
4
>>> 

检查参数是否为关键字参数的检查可能过度。