Search code examples
pythonpython-2.7keyword-argument

**kwargs and default arguments


class Klass:
  def __init__(self, **kwargs):
    self.func(variable=True, **kwargs)

  def func(self, variable=True, **kwargs):
    print(variable)


if __name__ == '__main__':
  Klass(variable=False)

I was wondering why I am getting TypeError: func() got multiple values for keyword argument 'variable'.

I am thinking it should print False because I override the default value of variable to False and pass kwargs along the way.


Solution

  • You can't pass the same argument twice, and variable=True, **kwargs does exactly that when kwargs contains a key for variable; in this case, you made the call effectively self.func(variable=True, variable=False) which is clearly wrong. Assuming you can't receive variable as a separate argument, e.g.:

    def __init__(self, variable=True, **kwargs):
        self.func(variable, **kwargs)
    
    # On Python 3, you can easily keep variable keyword-only with:
    def __init__(self, *, variable=True, **kwargs):
        self.func(variable, **kwargs)
    
    # while the Python 2 equivalent for keyword-only args is rather nastier:
    def __init__(self, *positional_forbidden, variable=True, **kwargs):
        if positional_forbidden:
            raise TypeError("__init__ takes 1 positional argument but {} were given".format(len(positional_forbidden)+1))
        self.func(variable, **kwargs)
    

    then the other approach is to set the default in the kwargs dict itself:

    def __init__(self, **kwargs):
        kwargs.setdefault('variable', True)  # Sets variable to True only if not passed by caller
        self.func(**kwargs)
    

    In Python 3.5, with PEP 448's additional unpacking generalizations, you could one-line this safely as:

    def __init__(self, **kwargs):
        self.func(**{'variable': True, **kwargs})
    

    because repeated keys are legal when creating a new dict (only the last occurrence of a key is kept), so you can create a brand new dict with unique mappings, then immediately unpack it.