相关旧问题:
在所有这些中,以下答案最吸引我。一个小的重写+示例(我使用的是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"
有没有办法解决这种类型错误,而不是沉默它? (即,它是否引起了有效的关注?)错误甚至说明了什么?
functools.update_wrapper()
期望 Callable
作为第一个参数,因此类型检查器不会犯错误。
update_wrapper()
应该通过分配/更新一些属性来使“包装器”的外观和行为(在运行时)与“包装的”相同。因此,包装器应该是可调用的,但您的 classprop
不是,即使在这种情况下它恰好适用于 classprop
实例。
沉默类型检查器在这里是一个有效的选择,至少如果你无意写这样的东西:
@classprop
def foo() -> int:
return 42
foo() # fail at runtime
作为折衷方案,您还可以使
classprop
实例仅在类型检查时可调用:
class classprop[T]:
...
if TYPE_CHECKING:
def __call__(self) -> T:
...
以正确的方式进行操作需要指定类型参数,因此如果您处于严格模式:
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"
如果你根本不关心这个类(即有问题的属性没有用),事情会变得更简单:
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 still throws a tantrum on this line, however.
return "world"