如何根据参数的值更改函数的暗示返回类型
我有一个函数,它通常返回一个它搜索的对象并执行一些其他操作。如果找不到匹配项,它会引发异常。通常,我不在乎它是否找到匹配项,但频率不够高,以至于我会考虑完全删除异常。因此,我做出的妥协是创建一个默认为 True
的参数 raise_on_failure
。如果为 False
,则返回 None
而不是引用。
def searchAndOperate(f, raise_on_failure: bool = True) -> Optional[Foo]:
# Search
result = ...
if result is None:
if raise_on_failure: raise ValueError("No results")
else: return None
# Operate
...
# Then return the result
return result
然而,这给我的类型提示带来了一些问题。我想这样做,以便如果 raise_on_failure
为 True
,该函数保证返回 Foo
,这样它就只是 如果
。raise_on_failure
为 False
,则可选[Foo]
我该怎么做?类似于以下代码片段的东西将是最理想的,但我也对其他想法持开放态度。
def searchAndOperate(
f,
raise_on_failure: bool = True
) -> Foo if raise_on_failure else Optional[Foo]:
...
I have a function where it usually returns an object that it searches for and performs some other actions. It raises an exception if it fails to find a match. Frequently, I don't care if it finds a match or not, but not frequently enough that I'd consider removing the exception entirely. As such, the compromise I've made is to create a parameter raise_on_failure
which defaults to True
. If it is False
, then None
is returned rather than the reference.
def searchAndOperate(f, raise_on_failure: bool = True) -> Optional[Foo]:
# Search
result = ...
if result is None:
if raise_on_failure: raise ValueError("No results")
else: return None
# Operate
...
# Then return the result
return result
However, this has caused some issues with my type hinting. I want to make it so that if raise_on_failure
is True
, the function is guaranteed to return a Foo
, such that it is only Optional[Foo]
if raise_on_failure
is False
.
How can I do this? Something akin to the following snippet would be the most desirable, but I'm open to other ideas too.
def searchAndOperate(
f,
raise_on_failure: bool = True
) -> Foo if raise_on_failure else Optional[Foo]:
...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
其他人已经提到,将函数
searchAndOperate
重构为两个单独的函数可能会更好。然而,为了完整起见,实际上可以使用 @overload 装饰器:
请注意,每个组合都有一个重载,再加上一个通用后备。对于更全面的函数签名,重载列表可能会变得很长。像 mypy 这样的静态类型检查器现在推断以下返回类型:
当不使用关键字参数或文字参数
True
或False
时,类型检查器正确推断两个专门的返回类型之一过载。当传递布尔表达式或变量时,类型检查器只能推断泛型重载(因为它无法在不运行代码的情况下知道实际值)。Others already mentioned that it might be better to refactor the function
searchAndOperate
into two separate functions.Just for completeness however, it's actually possible to create type annotations for the original behaviour by using the @overload decorator:
Note that there's an overload for each combination plus one generic fallback. For more comprehensive function signatures that overload list can get quite long. A static type checker like mypy now infers the following return types:
When using no keyword argument or the literal arguments
True
orFalse
, the type checker correctly infers one of the two specialised overloads. When passing a boolean expression or variable the type checker can infer the generic overload only (since it cannot know the actual value without running the code).从概念上讲,我认为根据参数而变化的类型提示没有意义。
类型提示
Optional[Foo]
已经封装了searchAndOperate
能够返回的所有内容:任何调用它的人都知道它将返回Foo
或无
。尝试根据 raise_on_failure 的值“拆分”返回类型会将函数的行为与其实现耦合起来。您应该做的是重构函数本身,使其行为明确。对当前类型提示的不满意源于searchAndOperate
尝试同时执行太多操作这一事实。例如,
searchAndOperate
可以分为单职责函数search
和operate
。或者您可以完全放弃 raise_on_failure 参数,并让调用者尝试/排除函数调用。如果它成为您经常做的事情,您可以将其包装为一个单独的函数,例如:这样,调用者就已经知道在没有找到结果时会发生什么。您可能认为这有点重复,但这是一种常见模式:
'abc'.index(s)
和'abc'.find(s)
之间的唯一区别> 的区别是,如果在'abc'
中找不到s
,第一个会引发异常,而第二个则返回默认值-1
在这种情况下。Conceptually, I do not think that type hints that change depending on a parameter makes sense.
The type hint
Optional[Foo]
already encapsulates everything thatsearchAndOperate
is able to return: anyone who calls it knows that it will return eitherFoo
orNone
. Trying to "split" the return types based to the value ofraise_on_failure
couples the function's behavior to its implementation. What you should be doing is refactoring the function itself so that its behavior is non-ambiguous. The dissatisfaction with your current type hint stems from the fact thatsearchAndOperate
tries to do too many things at once.For example,
searchAndOperate
may be split into single-responsibility functionssearch
andoperate
. Or you could just ditch theraise_on_failure
parameter entirely, and leave to the caller to try/except the function call. If it becomes something you do a lot, you could wrap it as a separate function, something like:This way, the caller already knows what to expect when no results are found. You might think that this is somewhat repetitive, but it is a common pattern: the only difference between
'abc'.index(s)
and'abc'.find(s)
is that the first raises an exception ifs
isn't found in'abc'
, and the second returns a default value of-1
in that case.我不确定这是否可能。
返回类型提示应该告诉您函数返回的内容,无需查看参数的值。
您本质上在此函数中实现了两种不同的行为:
Foo
或崩溃Foo
或None
如果您想将其编码为输入提示,您必须考虑这两种情况。这意味着函数是
Optional[Foo]
。如果您不希望这样,我建议将此函数拆分为两个不同的函数;一种是加注,另一种是加点:
I am not sure this is possible.
The return type hint is supposed to tell you, without looking at the values of the arguments, what the function returns.
You are essentially implementing two different behaviors in this function:
Foo
or crashFoo
orNone
If you want to encode this in a type hint, you have to consider both cases. That means that the function is
Optional[Foo]
.If you want it not to be, I would suggest splitting this function into two different functions; one that raises and one that does dot: