如何使用MYPY传递关键字args?

2024-04-19 15:23:45 发布

您现在位置:Python中文网/ 问答频道 /正文

假设:

def foo(biz: str = 'biz',baz: str = 'baz', *args:Any, **kwargs:Any)-> str:
   return biz + baz

我使用的方法如下:

def bar(ness: str = 'ness', *args, **kwargs):
   return foo(biz=ness, *args, **kwargs)

Note that this goal (specifying keywords and passing args + kwargs) shadowed some other misunderstanding on my part related to args: args, in python, can't be passed in after an arg with a default value (a keyword arg) has been set, as correctly pointed out below..

然后mypy版本0.812抛出如下错误:

$: mypy --ignore-missing-imports --disallow-untyped-defs --disallow-incomplete-defs

foo/foo.py:6: error: "foo" gets multiple values for keyword argument "biz"

是否有办法解决这个问题,尤其是,而不在下面的返回线以外的任何地方进行重大更改

   return foo(biz=ness, *args, **kwargs)

Tags: inreturnfoodefargargsanybaz
3条回答

这就是我最终想要的答案:

def bar(baz='ness', *args, **kwargs):
   return foo(*args, **dict(kwargs, baz=baz)

>>> bar()
bizness

根据您的回答,我将假设您希望linter传递bar的定义,并且您愿意为此目的对bar的实现/声明进行更改

对我来说,根本的问题是bar的实现是错误的-如果*args不是空的,那么调用foo(biz=ness, *args, **kwargs)将失败,并出现mypy报告的错误

def foo(biz: str = 'biz',baz: str = 'baz', *args:Any, **kwargs:Any)-> str:
    return biz + baz

def bar_from_question(ness: str = 'ness', *args, **kwargs):
    return foo(biz=ness, *args, **kwargs)
>>> bar_from_question("1")
1baz
>>> bar_from_question("1", "2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jean/Projects/python/test-mypy/test.py", line 7, in bar_from_question
     return foo(biz=ness, *args, **kwargs)
TypeError: foo() got multiple values for argument 'biz'

对我来说,你有三个选择:

  1. 通过不再传递args修复对foo的调用。这样的更改不会导致功能损失,因为在bar中有一个非空的args无论如何都会引发一个TypeError。在这种情况下,您还可以从bar的签名中删除args(同样,在不丢失功能的情况下-您只需将对foo的调用触发的TypeError替换为对bar的调用中的TypeError

    def bar_do_not_pass_args_to_foo(ness: str = 'ness', *args, **kwargs):
        return foo(biz=ness, **kwargs)
    
    def bar_remove_args_from_signature(ness: str = 'ness', **kwargs):
        return foo(biz=ness, **kwargs)
    
  2. 通过将biz作为位置参数而不是命名参数传递,修复对foo的调用:

    def bar_pass_biz_as_positional(ness: str = 'ness', *args, **kwargs):
        return foo(ness, *args, **kwargs)
    
  3. 向mypy隐瞒问题(我从您的回答中了解)

    def bar_hide_from_mypy(ness: str = 'ness', *args, **kwargs):
        kwargs['biz'] = ness
        return foo(*args, **kwargs)
    

    但是,如果目标只是防止mypy报告错误,则有以下语法:

    def bar_suppress_mypy(ness: str = 'ness', *args, **kwargs):
        return foo(biz=ness, *args, **kwargs)  # type: ignore
    

现在,如果我在运行时解释器中比较每个建议的结果:

>>> bar_do_not_pass_args_to_foo("1")
'1baz'
>>> bar_do_not_pass_args_to_foo("1", "2")
'1baz'
>>> bar_remove_args_from_signature("1")
'1baz'
>>> bar_remove_args_from_signature("1", "2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bar_remove_args_from_signature() takes from 0 to 1 positional arguments but 2 were given
>>> bar_pass_biz_as_positional("1")
'1baz'
>>> bar_pass_biz_as_positional("1", "2")
'12'
>>> bar_hide_from_mypy("1")
'1baz'
>>> bar_hide_from_mypy("1", "2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jean/Projects/python/test-mypy/test.py", line 20, in bar_hide_from_mypy
    return foo(*args, **kwargs)
TypeError: foo() got multiple values for argument 'biz'
>>> bar_suppress_mypy("1")
'1baz'
>>> bar_suppress_mypy("1", "2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jean/Projects/python/test-mypy/test.py", line 23, in bar_suppress_mypy
    return foo(biz=ness, *args, **kwargs)  # type: ignore
TypeError: foo() got multiple values for argument 'biz'

你可以看到:

  • 对于bar_from_question返回结果的每一种情况,其他每一个bar_...实现都返回相同的结果
  • 除了bar_from_question之外的每个实现都对mypy保持沉默,我认为mypy是您想要的

在您的情况下,我会寻求根本问题的解决方案(即,应用要点1或2),而不仅仅是试图强制mypy(要点3)

尽管对foo的调用有顺序,但在考虑关键字参数biz之前,args中的值被分配给位置参数。这意味着bar可以用位置参数调用,可以在ness被赋值之前将一个值赋值给biz

也就是说,mypy正在提前捕获以下潜在的运行时错误:

>>> bar("1", "2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in bar
TypeError: foo() got multiple values for argument 'biz'

字符串"1"被分配给ness,字符串"2"成为args的第一个元素。当调用foo时,将args的元素分配给位置参数bizbaz;在这种情况下,只有biz获得一个值,即"2"。只有这样,才会考虑关键字参数biz=ness,此时biz已被考虑

相关问题 更多 >