我们的代码在 Python 对象的私有属性、受保护属性和公共属性方面非常严格,遵循私有属性以
__
开头的约定(因此被修饰为包含类名),受保护属性以 _
开头公共属性不以_
.开头
但是,我们的一个常见模式是希望将属性公开为私有可写但公开可读,并受静态类型注释的约束。由于我们广泛使用
override
包来对子类的方法进行类型检查,这使情况变得更加复杂。
我们的代码因此充满了
class C:
self.__attribute: T
def __init__(self, attribute: T):
self.__attribute = attribute
@property
def attribute(self) -> T:
return self.attribute
代替简单的数据类。
有没有好的模式来最小化样板文件?怎么样
class B(metaclass=ABCMeta):
@property
@abstractmethod
def weird_attribute(self):
raise NotImplementedError
class D(B):
self.__weird_attribute: T
def __init__(self, wattribute: T):
self.__weird_attribute = wattribute
@property # type: ignore
@overrides
def weird_attribute(self) -> T:
return self.__weird_attribute
class E(B):
@property # type: ignore
@overrides
def weird_attribute(self) -> T:
return 1
这种风格真的让我很烦恼,因为我们尝试使用静态类型检查来很好地掌握我们的代码——然后这种糟糕的模式需要一个
# type: ignore
,因为属性不能被修饰,重写也不能是属性。而且它甚至不简洁。
有出路吗?
从 Python 3.10 开始,这很容易使用数据类和描述符:
import dataclasses
from typing import cast, Generic, TypeVar
_T = TypeVar('_T')
class Prop(Generic[_T]):
_name: str
_mangeled_name: str
def __set_name__(self, owner: type, name: str) -> None:
self._name = name
self._mangeled_name = f'_{owner.__name__}__{name}'
def __get__(self, instance: object | None, owner: type) -> _T:
if instance is not None:
try:
return cast(_T, getattr(instance, self._mangeled_name))
except AttributeError:
pass
raise AttributeError(self._name)
def __set__(self, instance: object, value: _T) -> None:
if hasattr(instance, self._mangeled_name):
raise AttributeError(f"can't set attribute {self._name!r}")
setattr(instance, self._mangeled_name, value)
@dataclasses.dataclass
class C:
attribute: Prop[str] = Prop()
setter 允许对属性进行单个赋值,这将发生在
__init__()
装饰器生成的 dataclasses()
方法中。
reveal_type(C.attribute)
显示类型为 str
和 attribute
将在构造函数中需要。
或者跳过数据类并使用下面的示例,如果以下任何一项适用:
class C(Generic[_T]):
attribute: Prop[_T] = Prop()
def __init__(self, attribute: _T) -> None:
self.attribute = attribute
使用 Python 3.10.9 和 mypy 1.0.1. 测试