首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带条件参数的示教

带条件参数的示教
EN

Stack Overflow用户
提问于 2022-11-28 04:51:42
回答 3查看 67关注 0票数 2

我试图使用带有条件参数的函数进行键入,该函数的工作方式如下:

代码语言:javascript
复制
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查询时,它抱怨:

代码语言:javascript
复制
Incompatible types in assignment (expression has type "Union[Foo, Bar, None]", variable has type "Union[Foo, Bar]"

(我猜是因为参数类型提示中的Optional。)

如果然后添加None作为foo_or_bar的类型提示,则错误如下:

代码语言:javascript
复制
error: Item "None" of "Union[Foo, Bar, None]" has no attribute "some_param_to_check"

我如何解决这个问题,以使类型停止抱怨(同时仍然保留类型提示)?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-11-28 05:54:48

我很确定这只是个典型的问题。它的类型推断系统不够聪明,无法认识到引发异常的if not foo and not bar块稍后排除了双重None情况(因为它不能孤立地推断出有关这两种类型的任何内容)。似乎没有一种直接修正类型暗示的好方法,但是您可以稍微修改一下逻辑,以便更清楚地区分这些情况,而且mypy应该更好地理解它:

代码语言:javascript
复制
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变量放在一起,只需在ifelif块中添加两个不同的函数调用,并酌情使用foobar

票数 3
EN

Stack Overflow用户

发布于 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

代码语言:javascript
复制
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_bNone时,您将返回False

但请不要以任何对你的申请有意义的方式处理。

票数 1
EN

Stack Overflow用户

发布于 2022-11-28 10:36:55

把我的0.02美元加到@Blckknght的回答上:

这解决了内部类型检查的问题(如果您修复了缺少的注释,在函数体内没有错误),但是帮不上忙外部调用程序。

要实现键入“一种或另一种,但绝不是任何一种”,您可以使用重载:

代码语言:javascript
复制
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

瞧:

代码语言:javascript
复制
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

这是操场链接

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74596129

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档