如何使装饰器在函数体中缩小类型?

huangapple go评论73阅读模式
英文:

(How) can I make a decorator narrow a type in the function body?

问题

我正在实现一个装饰器来替代重复的早期退出检查,例如:

if self.data is None:
    self.log_something()
    return

这个装饰器 @early_exit_if(data_is_none) 确保在 do_something_with_data 方法体内,data 不是 None。但是,mypy 并不知道这一点。

如何让 mypy 知道(最好是从装饰器内部)data 不会是 None

我知道 assert self.data is not None 可以工作,但我不喜欢重复这个检查。

编辑:我刚刚了解到 TypeGuard,我想知道这种方法是否可以与装饰器结合使用。

英文:

I am implementing a decorator to replace repeated early-exit checks such as

if self.data is None:
    self.log_something()
    return

at the top of my methods. This approach (highly simplified here) does work nicely:

"""early exit decorator."""
from collections.abc import Callable

Method = Callable[["Class"], None]
Condition = Callable[["Class"], bool]

def early_exit_if(condition: Condition) -> Callable[[Method], Method]:
    def decorator(method: Method) -> Method:
        def wrapper(instance: "Class") -> None:
            if condition(instance):
                return
            method(instance)
        return wrapper
    return decorator

class Class:
    def __init__(self) -> None:
        self.data: list[int] | None = None

    @staticmethod
    def data_is_none(instance: "Class") -> bool:
        return instance.data is None

    @early_exit_if(data_is_none)
    def do_something_with_data(self) -> None:
        self.data.append(0)

if __name__ == "__main__":
    Class().do_something_with_data()

In short, @early_exit_if(data_is_none) ensures that data is not None in the body of do_something_with_data. However, mypy does not know that:

> bug.py:30: error: Item "None" of "list[int] | None" has no attribute "append" [union-attr]

How can I let mypy know (ideally, from within the decorator) that data is not None?

I know that assert self.data is not None works, but I dislike duplicating the check.

Edit: I just learned about TypeGuards, and I wonder if this approach can be combined with decorators.

答案1

得分: 1

Here is the translated content:

实际上,您可能已经有了最佳答案:

assert self.data is not None

这在mypy类型缩小等方面有很好的文档支持。

请记住,这并不一定会重复检查,因为assert通常用于开发阶段,在生产中,您可以运行“python -O”(优化模式),断言将被忽略,因此不会影响性能。

英文:

Actually, you may already have the best answer:

assert self.data is not None

Is well documented by mypy in type narrowing among other things.

Remember that it doesn't necessarily duplicate the check as assert is meant to be use mostly at development time, in production you can run "python -O" (optimized mode) and asserts are just ignored, so it doesn't impact performance at all.

huangapple
  • 本文由 发表于 2023年6月29日 15:30:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76578897.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定