覆盖 Python 中的关键字参数

Override a keyword argument in Python

Python版本:3.10.0

我有多个函数使用相同的参数,大部分也有相同的值,所以我决定使用关键字参数来减少提供给函数的参数数量。

但是,现在我 运行 进入了这个问题:如果我想用自定义值覆盖一些关键字参数,而不改变 'global defaults' 怎么办?

使用一个简化的例子:

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)

我已将所有常用关键字参数存储在一个字典中,该字典提供给所有使用常用参数的函数。

但是,当我尝试覆盖一些关键字参数时,我收到了这个错误:

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'

意思是 Python 无法决定它会使用哪个提供的值。

解决这个问题的最干净(最优雅)的方法是什么?

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)

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

在这种情况下,我会使用 functools.partial(来自标准库中的 functools 模块)考虑以下简单示例

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 提供部分对象,其行为类似于提供给它的具有设置默认值的函数。

您可以使用更新后的值创建新字典:

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)
  
  

输出:

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'}
>>> foo = {'bar': 'baz'}
>>> {**foo, 'bar': 42}
{'bar': 42}
>>> {'bar': 42, **foo}
{'bar': 'baz'}

所以:

func("test", "some-another-value", **{**common, "special": "SOMETHING_ELSE"})