'Override a keyword argument in Python [duplicate]

Python version: 3.10.0

I have multiple functions using same arguments, mostly with same values also, so I decided to use keyword arguments instead to reduce the amount of parameters provided to the functions.

However, now I ran into this issue: What if I would like to override some of the keyword arguments with custom values, without altering the 'global defaults'?

Using a simplified example:

def func(arg1, arg2, **kwargs):
    print(f"Received: {arg1} {arg2} {kwargs}")
    print(f"Special: {kwargs.get('special')}")


def func2(arg1, arg2, **kwargs):
    print(f"Received: {arg1} {arg2} {kwargs}")
    print(f"Special: {kwargs.get('special')}")


common = {
    'param1': 10,
    'param2': True,
    'special': "SPECIAL_DEFAULT"
}

if __name__ == '__main__':
    func("test", "some-value", **common)
    func2(99, None, **common)

    # For example, for this call, I want to override one of the common arguments with a different value
    # Without altering var common that is used globally
    # However, it throws TypeError because 'special' is defined in multiple places now
    func("test", "some-another-value", special="SOMETHING_ELSE", **common)

I have stored all common keyword arguments in a dict that is provided into all functions using the common arguments.

However, when I try to override some of the keyword arguments Im getting this error:

Traceback (most recent call last):
  line 24, in <module>
    func("test", "some-another-value", special="SOMETHING_ELSE", **common)
TypeError: __main__.func() got multiple values for keyword argument 'special'

Meaning that Python cant decide which of the provided values it would use.

What would be the cleanest (most elegant) way to overcome this issue?



Solution 1:[1]

What would be the cleanest (most elegant) way to overcome this issue?

I would use functools.partial (from functools module in standard library) in this case consider following simple example

import functools
def volume(**kwargs):
    return kwargs["x"]*kwargs["y"]*kwargs["z"]
volume = functools.partial(volume, x=1, y=1, z=1)
print(volume())  # 1
print(volume(z=5))  # 5
print(volume(x=2,y=3,z=4))  # 24

functools.partial provide partial object, which behave like function provided to it with set default values.

Solution 2:[2]

You can create a new dictionary with the updated values:

def func(arg1, arg2, **kwargs):
    print(f"Received: {arg1} {arg2} {kwargs}")
    print(f"Special: {kwargs.get('special')}")


def func2(arg1, arg2, **kwargs):
    print(f"Received: {arg1} {arg2} {kwargs}")
    print(f"Special: {kwargs.get('special')}")


common = {
    'param1': 10,
    'param2': True,
    'special': "SPECIAL_DEFAULT"
}

if __name__ == '__main__':
  func("test", "some-value", **common)
  func2(99, None, **common)
  spcl = dict(common)
  spcl['special'] = "SOMETHING_ELSE"
  func("test", "some-another-value",  **spcl)
  
  print(common)
  print(spcl)
  
  

Outputs:

Received: test some-value {'param1': 10, 'param2': True, 'special': 'SPECIAL_DEFAULT'}
Special: SPECIAL_DEFAULT
Received: 99 None {'param1': 10, 'param2': True, 'special': 'SPECIAL_DEFAULT'}
Special: SPECIAL_DEFAULT
Received: test some-another-value {'param1': 10, 'param2': True, 'special': 'SOMETHING_ELSE'}
Special: SOMETHING_ELSE
{'param1': 10, 'param2': True, 'special': 'SPECIAL_DEFAULT'}
{'param1': 10, 'param2': True, 'special': 'SOMETHING_ELSE'}

Solution 3:[3]

def func(arg1, arg2, **kwargs):
    print(f"Received: {arg1} {arg2} {kwargs}")
    print(f"Special: {kwargs.get('special')}")


common = {"param1": 10, "param2": True, "special": "SPECIAL_DEFAULT"}
special_params = {**common, "special": "SOMETHING_ELSE"}

if __name__ == "__main__":
    func("test", "some-value", **common)
    func(99, None, **common)
    func("test", "some-another-value", **special_params)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Daweo
Solution 2 Devang Sanghani
Solution 3