如何使用 MYPY 传递关键字参数?
How does one pass keyword args with MYPY?
假设:
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 行之外, 没有对任何地方进行重大更改?
return foo(biz=ness, *args, **kwargs)
尽管调用 foo
时的顺序,args
中的值在考虑关键字参数 biz
之前分配给位置参数。这意味着 bar
可以 可以 使用位置参数调用, 可以 有一个值分配给 biz
之前ness
分配给它。
也就是说,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
的元素赋值给位置参数biz
和baz
;在这种情况下,只有 biz
得到一个值,即 "2"
。只有这样才会考虑关键字参数 biz=ness
,此时 biz
已经被考虑在内。
这就是我要找的答案,最后:
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'
对我来说,你有三个选择:
通过不再传递 args
来修复对 foo
的调用。这样的更改不会导致功能损失,因为 bar
中的非空 args
无论如何都会引发 TypeError
。在这种情况下,您还可以从 bar
的签名中删除 args
(同样,在不丢失功能的情况下 - 您只需将调用 foo
触发的 TypeError
替换为TypeError
在对 bar
)
的调用中
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)
通过将 biz
作为位置参数而不是命名参数传递来修复对 foo
的调用:
def bar_pass_biz_as_positional(ness: str = 'ness', *args, **kwargs):
return foo(ness, *args, **kwargs)
从 mypy 隐藏问题(据我从你的回答中了解到)
def bar_hide_from_mypy(ness: str = 'ness', *args, **kwargs):
kwargs['biz'] = ness
return foo(*args, **kwargs)
但是,如果 objective 只是为了防止 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
returns 结果的情况,每隔 bar_...
实施 returns 相同的结果;
- 除
bar_from_question
之外的每个实现都对 mypy 保持沉默,我的印象是你想要的;
在你的情况下,我会寻求根本问题的解决方案(即应用要点 1 或 2),而不是仅仅试图强制 mypy(要点 3)。
假设:
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 行之外, 没有对任何地方进行重大更改?
return foo(biz=ness, *args, **kwargs)
尽管调用 foo
时的顺序,args
中的值在考虑关键字参数 biz
之前分配给位置参数。这意味着 bar
可以 可以 使用位置参数调用, 可以 有一个值分配给 biz
之前ness
分配给它。
也就是说,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
的元素赋值给位置参数biz
和baz
;在这种情况下,只有 biz
得到一个值,即 "2"
。只有这样才会考虑关键字参数 biz=ness
,此时 biz
已经被考虑在内。
这就是我要找的答案,最后:
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'
对我来说,你有三个选择:
通过不再传递
的调用中args
来修复对foo
的调用。这样的更改不会导致功能损失,因为bar
中的非空args
无论如何都会引发TypeError
。在这种情况下,您还可以从bar
的签名中删除args
(同样,在不丢失功能的情况下 - 您只需将调用foo
触发的TypeError
替换为TypeError
在对bar
)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)
通过将
biz
作为位置参数而不是命名参数传递来修复对foo
的调用:def bar_pass_biz_as_positional(ness: str = 'ness', *args, **kwargs): return foo(ness, *args, **kwargs)
从 mypy 隐藏问题(据我从你的回答中了解到)
def bar_hide_from_mypy(ness: str = 'ness', *args, **kwargs): kwargs['biz'] = ness return foo(*args, **kwargs)
但是,如果 objective 只是为了防止 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
returns 结果的情况,每隔bar_...
实施 returns 相同的结果; - 除
bar_from_question
之外的每个实现都对 mypy 保持沉默,我的印象是你想要的;
在你的情况下,我会寻求根本问题的解决方案(即应用要点 1 或 2),而不是仅仅试图强制 mypy(要点 3)。