部分:不允许覆盖给定的关键字参数

partial: disallow overriding given keyword arguments

有没有办法禁止在部分代码中覆盖给定的关键字参数?假设我想创建函数 bar,它始终将 a 设置为 1。在以下代码中:

from functools import partial

def foo(a, b):
  print(a)
  print(b)

bar = partial(foo, a=1)
bar(b=3) # This is fine and prints 1, 3
bar(a=3, b=3) # This prints 3, 3

你可以愉快地调用bar并设置a3。是否可以从 foo 中创建 bar 并确保调用 bar(a=3, b=3) 会引发错误或静默忽略 a=3 并继续使用 a=1部分?

你真的需要使用partial吗? 您通常可以使用更少的参数定义一个新函数 bar。

喜欢:

def bar(a):
  b = 1
  foo(a, b)

这会报错。

或喜欢:

def bar(a, b=1):
  b = 1 #ignored provided b
  foo(a, b)

这将忽略 b.

编辑:如果你想将它们连成一行,请使用 lambda: 喜欢:bar = lambda a:foo(a,b=1)bar = lambda a,b=1:foo(a,b=1)

这是设计partial 的文档说(强调我的):

functools.partial(func, /, *args, **keywords)

Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args. If additional keyword arguments are supplied, they extend and override keywords.

如果您不想这样,您可以手动拒绝冻结关键字参数:

def freeze(f, **kwargs):
    frozen = kwargs
    def wrapper(*args, **kwargs):
        kwargs.update(frozen)
        return f(*args, **kwargs)
    return wrapper

您现在可以:

>>> baz = freeze(foo, a=1)
>>> baz(b=3, a=2)
1
3

如果您想在覆盖发生时引发错误,您可以创建 partial:

的自定义实现
class _partial:
   def __init__(self, f, **kwargs):
      self.f, self.kwargs = f, kwargs
   def __call__(self, **kwargs):
      if (p:=next((i for i in kwargs if i in self.kwargs), None)) is not None:
         raise ValueError(f"parameter '{p}' already specified")
      return self.f(**self.kwargs, **kwargs)

def foo(a, b):
   print(a)
   print(b)

>>> bar = _partial(foo, a=1)
>>> bar(b=3)
1
3
>>> bar(a=3, b=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __call__
ValueError: parameter 'a' already specified