我试图使用带有条件参数的函数进行键入,该函数的工作方式如下:
from typing import Optional, Union
class Foo:
some_param_to_check: str = 'foo_name'
one_param_exclusive_to_foo: int
class Bar:
some_param_to_check: str = 'bar_name'
another_param_exclusive_to_bar: str
def some_process_that_returns_a_bool(
f_or_b: Union[Foo, Bar],
a_name: str,
) -> bool:
return f_or_b.some_param_to_check == a_name
def do_something_with_foo_or_bar(
foo: Optional[Foo],
bar: Optional[Bar],
some_name: str,
) -> bool:
if not foo and not bar:
raise ValueError('You need to specify either "foo" or "bar".')
# I added this explicit type hint after the first error, hoping it would solve the issue:
foo_or_bar: Union[Foo, Bar] # later becomes Union[Foo, Bar, None]
foo_or_bar = foo if foo else bar
return some_process_that_returns_a_bool(foo_or_bar, some_name)
foo_obj = Foo()
bar_obj = Bar()
# This will work:
do_something_with_foo_or_bar(foo_obj, bar_obj, 'test_string')
# This will also work:
do_something_with_foo_or_bar(foo_obj, None, 'test_string')
# This too:
do_something_with_foo_or_bar(None, bar_obj, 'test_string')
# But this should not:
do_something_with_foo_or_bar(None, None, 'test_string')要添加更多的上下文:
该函数的工作方式是期望foo,或者,如果不可用,则使用bar。如果foo不是None,那么bar基本上就会被忽略。
在向mypy查询时,它抱怨:
Incompatible types in assignment (expression has type "Union[Foo, Bar, None]", variable has type "Union[Foo, Bar]"(我猜是因为参数类型提示中的Optional。)
如果然后添加None作为foo_or_bar的类型提示,则错误如下:
error: Item "None" of "Union[Foo, Bar, None]" has no attribute "some_param_to_check"我如何解决这个问题,以使类型停止抱怨(同时仍然保留类型提示)?
发布于 2022-11-28 05:54:48
我很确定这只是个典型的问题。它的类型推断系统不够聪明,无法认识到引发异常的if not foo and not bar块稍后排除了双重None情况(因为它不能孤立地推断出有关这两种类型的任何内容)。似乎没有一种直接修正类型暗示的好方法,但是您可以稍微修改一下逻辑,以便更清楚地区分这些情况,而且mypy应该更好地理解它:
def do_something_with_foo_or_bar(
foo: Optional[Foo],
bar: Optional[Bar],
some_name: str,
) -> bool:
foo_or_bar: Union[Foo, Bar]
if foo:
foo_or_bar = foo
elif bar:
foo_or_bar = bar
else:
raise ValueError('You need to specify either "foo" or "bar".')
return some_process_that_returns_a_bool(foo_or_bar, some_name)您还可以将foo_or_bar变量放在一起,只需在if和elif块中添加两个不同的函数调用,并酌情使用foo或bar。
发布于 2022-11-28 05:06:52
您将得到错误,因为在some_process_that_returns_a_bool中,您试图访问一个可以是None的值的some_param_to_check,而None没有该属性。
如果some_process_that_returns_a_bool应该接受None作为f_or_b的一个可能值,那么您应该在尝试访问任何属性之前检查None。
def some_process_that_returns_a_bool(
f_or_b: Union[Foo, Bar, None],
a_name: str,
) -> bool:
if f_or_b is None:
# Handle None...
return False
else:
return f_or_b.some_param_to_check == a_name这样,您将只在f_or_b不是None时才尝试访问False,而当f_or_b是None时,您将返回False。
但请不要以任何对你的申请有意义的方式处理。
发布于 2022-11-28 10:36:55
把我的0.02美元加到@Blckknght的回答上:
这解决了内部类型检查的问题(如果您修复了缺少的注释,在函数体内没有错误),但是帮不上忙外部调用程序。
要实现键入“一种或另一种,但绝不是任何一种”,您可以使用重载:
from typing import Optional, Union, overload
class Foo:
some_param_to_check: str = 'foo_name'
one_param_exclusive_to_foo: int
class Bar:
some_param_to_check: str = 'bar_name'
another_param_exclusive_to_bar: str
def some_process_that_returns_a_bool(
f_or_b: Union[Foo, Bar],
a_name: str,
) -> bool:
return f_or_b.some_param_to_check == a_name
@overload
def do_something_with_foo_or_bar(
foo: Foo,
bar: Optional[Bar],
some_name: str,
) -> bool: ...
@overload
def do_something_with_foo_or_bar(
foo: Optional[Foo],
bar: Bar,
some_name: str,
) -> bool: ...
def do_something_with_foo_or_bar(
foo: Optional[Foo],
bar: Optional[Bar],
some_name: str,
) -> bool:
foo_or_bar: Union[Foo, Bar] # This annotation was missing
if foo:
foo_or_bar = foo
elif bar:
foo_or_bar = bar
else:
raise ValueError('You need to specify either "foo" or "bar".')
return some_process_that_returns_a_bool(foo_or_bar, some_name)
do_something_with_foo_or_bar(Foo(), Bar(), '')
do_something_with_foo_or_bar(None, Bar(), '')
do_something_with_foo_or_bar(Foo(), None, '')
do_something_with_foo_or_bar(None, None, '') # Line 51瞧:
main.py:51: error: No overload variant of "do_something_with_foo_or_bar" matches argument types "None", "None", "str" [call-overload]
main.py:51: note: Possible overload variants:
main.py:51: note: def do_something_with_foo_or_bar(foo: Foo, bar: Optional[Bar], some_name: str) -> bool
main.py:51: note: def do_something_with_foo_or_bar(foo: Optional[Foo], bar: Bar, some_name: str) -> bool这是操场链接。
https://stackoverflow.com/questions/74596129
复制相似问题