
在这种情况下,interface.foo 和 interface.bar 会被标记为 (variable) foo/bar: any,即使它们应该是 (property) foo/bar: str。这会导致类型检查器无法正确识别属性的类型。
为了解决这个问题,我们可以自定义一个泛型 property 类,它可以保留类型信息。以下是一个示例实现:
from typing import Any, Generic, TypeVar, overload, cast, Callable
T = TypeVar('T') # The return type
I = TypeVar('I') # The outer instance's type
class Property(property, Generic[I, T]):
def __init__(
self,
fget: Callable[[I], T] | None = None,
fset: Callable[[I, T], None] | None = None,
fdel: Callable[[I], None] | None = None,
doc: str | None = None
) -> None:
super().__init__(fget, fset, fdel, doc)
@overload
def __get__(self, instance: None, owner: type[I] | None = None) -> Callable[[I], T]:
...
@overload
def __get__(self, instance: I, owner: type[I] | None = None) -> T:
...
def __get__(self, instance: I | None, owner: type[I] | None = None) -> Callable[[I], T] | T:
return cast(Callable[[I], T] | T, super().__get__(instance, owner))
def __set__(self, instance: I, value: T) -> None:
super().__set__(instance, value)
def __delete__(self, instance: I) -> None:
super().__delete__(instance)这个 Property 类继承自 Python 内置的 property 类,并使用泛型来指定 getter 和 setter 方法的类型。I 代表外部实例的类型,T 代表返回值的类型。
有了自定义的 Property 类,我们可以修改原始的代码,使用它来创建属性:
from collections.abc import Callable
Getter = Callable[['Interface'], str]
Setter = Callable[['Interface', str], None]
def complex_property(name: str) -> tuple[Getter, Setter]:
def _getter(self: Interface) -> str:
return name # Replace ... with actual getter logic
def _setter(self: Interface, value: str) -> None:
pass # Replace ... with actual setter logic
return _getter, _setter
class Interface:
foo = Property(*complex_property("foo"))或者,也可以直接在 property_factory 中使用 Property 类:
立即学习“Python免费学习笔记(深入)”;
from __future__ import annotations
from typing import Callable
class Interface:
def property_factory(name: str) -> Property['Interface', str]:
"""Create a property depending on the name."""
@property
def _complex_property(self: Interface) -> str:
# Do something complex with the provided name
return name
@_complex_property.setter
def _complex_property(self: Interface, _: str):
pass
return Property(_complex_property.fget, _complex_property.fset)
foo = property_factory("foo") # Works just like an actual property
bar = property_factory("bar")这样,类型检查器就能正确识别 Interface.foo 和 Interface.bar 的类型为 str。
以下是一些使用 mypy 和 pyright 进行类型检查的示例测试:
reveal_type(Interface.foo) # mypy => (Interface) -> str
# pyright => (Interface) -> str
reveal_type(Interface.bar) # mypy => (Interface) -> str
# pyright => property
instance = Interface()
reveal_type(instance.foo) # mypy + pyright => str
reveal_type(instance.bar) # mypy + pyright => str
instance.foo = 42 # mypy => error: Incompatible types in assignment
# pyright => error: "Literal[42]" is incompatible with "str" ('foo' is underlined)
instance.bar = 42 # mypy => error: Incompatible types in assignment
# pyright => error: "Literal[42]" is incompatible with "str" ('42' is underlined)
instance.foo = 'lorem' # mypy + pyright => fine
instance.bar = 'ipsum' # mypy + pyright => fine这些测试表明,使用自定义的 Property 类可以确保类型检查器能够正确识别属性的类型,并在类型不匹配时发出错误。
通过自定义泛型 property 类,我们可以解决在使用工厂方法动态创建类属性时类型提示丢失的问题。这种方法可以提高代码的可维护性和健壮性,并确保类型检查器能够准确识别属性的类型。在实际开发中,可以根据需要扩展自定义 Property 类,以支持更多的功能和类型。
以上就是如何为 Python 类中使用工厂方法创建的属性添加类型提示的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号