通过类型检查的只读类属性,现代方式(3.11+)

问题描述 投票:0回答:1

相关旧问题:

在所有这些中,以下答案最吸引我。一个小的重写+示例(我使用的是Python 3.12+):

from functools import update_wrapper
from typing import Callable


class classprop[T]:
    def __init__(self, method: Callable[..., T]):
        self.method = method
        update_wrapper(self, method)

    def __get__(self, obj, cls=None) -> T:
        if cls is None:
            cls = type(obj)
        return self.method(cls)


class MyClass:
    @classprop
    def hello(self) -> str:
        return "world"


x = MyClass()
x.hello

这会引发类型错误(在原始答案中被静音):

Argument of type "Self@classprop[T@classprop]" cannot be assigned to 
parameter "wrapper" of type "(**_PWrapper@update_wrapper) -> _RWrapper@update_wrapper" 
in function "update_wrapper"
  Type "Self@classprop[T@classprop]" is incompatible with 
  type "(**_PWrapper@update_wrapper) -> _RWrapper@update_wrapper"

有没有办法解决这种类型错误,而不是沉默它? (即,它是否引起了有效的关注?)错误甚至说明了什么?

python mypy python-typing pyright
1个回答
0
投票

functools.update_wrapper()
期望
Callable
作为第一个参数,因此类型检查器不会犯错误。

update_wrapper()
应该通过分配/更新一些属性来使“包装器”的外观和行为(在运行时)与“包装的”相同。因此,包装器应该是可调用的,但您的
classprop
不是,即使在这种情况下它恰好适用于
classprop
实例。

沉默类型检查器在这里是一个有效的选择,至少如果你无意写这样的东西:

@classprop
def foo() -> int:
    return 42

foo()  # fail at runtime

作为折衷方案,您还可以使

classprop
实例仅在类型检查时可调用:

(游乐场链接:MypyPyright

class classprop[T]:
    ...

    if TYPE_CHECKING:
        def __call__(self) -> T:
            ...

以正确的方式进行操作需要指定类型参数,因此如果您处于严格模式:

(游乐场链接:MypyPyright

class classprop[O, T]:
    method: Callable[[type[O]], T]

    # `Any` is actually supposed to be `type[O]`.
    # However, type checkers won't be able to figure out
    # that `cls` is `type[O]` and not `O`.
    def __init__(self, method: Callable[[Any], T]):
        self.method = method
        update_wrapper(self, method)

    def __get__(self, obj: O | None, cls: type[O] | None = None) -> T:
        ...
    
    def __call__(self, instance: O) -> T:
        return self.__get__(instance)
class MyClass:
    @classprop['MyClass', str]
    def hello(self) -> str:
        return "world"

如果你根本不关心这个类(即有问题的属性没有用),事情会变得更简单:

(游乐场链接:MypyPyright

class classprop[T]:
    method: Callable[[], T]

    def __init__(self, method: Callable[[], T]):
        self.method = method
        update_wrapper(self, method)

    def __get__(self, obj: Any, cls: type[Any] | None = None) -> T:
        return self.method()
    
    def __call__(self, instance: Any) -> T:
        return self.method()
class MyClass:
    @classprop
    def hello() -> str:  # mypy => error: Method must have at least one argument.
        return "world"
© www.soinside.com 2019 - 2024. All rights reserved.