类型化场景下Python只读属性低样板代码实现方案咨询
解决方案:私有写入、公有只读属性的简洁实现(兼容类型注解与overrides)
核心问题梳理
你需要实现仅私有写入、公有只读的属性,同时满足:
- 严格遵循Python私有/保护/公有属性规范(
__前缀私有) - 支持静态类型注解
- 兼容
overrides装饰器的子类方法检查 - 减少重复样板代码
方案1:调整装饰器顺序+修正类型注解(解决overrides与property冲突)
之前的# type: ignore是因为装饰器顺序错误,overrides需要直接装饰属性的getter方法,而非property对象。正确的写法是将@overrides放在@property上方,并明确父类的抽象属性类型:
from abc import ABCMeta, abstractmethod from overrides import overrides from typing import TypeVar T = TypeVar('T') class B(metaclass=ABCMeta): @property @abstractmethod def weird_attribute(self) -> T: raise NotImplementedError class D(B): __weird_attribute: T def __init__(self, wattribute: T): self.__weird_attribute = wattribute @overrides @property def weird_attribute(self) -> T: return self.__weird_attribute class E(B): @overrides @property def weird_attribute(self) -> int: return 1 # 静态检查会自动校验返回类型是否匹配父类的T约束
关键说明
- 父类的抽象属性必须添加明确的类型注解,这样
overrides和类型检查工具(如mypy)才能正确校验子类实现 - 装饰器顺序:
@overrides在前,@property在后,确保overrides检查的是原始方法的签名,而非property实例 - 无需
# type: ignore,静态类型检查可正常工作
方案2:使用自定义装饰器减少样板代码
如果大量重复私有属性+只读property的代码,可编写一个自定义装饰器自动生成相关逻辑:
from typing import TypeVar, Callable, Any from overrides import overrides T = TypeVar('T') def readonly_private_property(name: str) -> Callable[[Any], property]: def decorator(func: Callable[[Any], T]) -> property: # 生成私有属性名称(Python名称修饰后的格式) private_name = f"_{func.__qualname__.split('.')[0]}{name}" def getter(self: Any) -> T: return getattr(self, private_name) # 绑定getter的签名和类型注解 getter.__annotations__ = func.__annotations__ getter.__name__ = func.__name__ return property(getter) return decorator # 使用示例 class C: __attribute: str def __init__(self, attribute: str): self.__attribute = attribute @readonly_private_property("__attribute") def attribute(self) -> str: pass # 无需写返回逻辑,装饰器自动处理 # 子类兼容overrides的写法 from abc import ABCMeta, abstractmethod class B(metaclass=ABCMeta): @property @abstractmethod def weird_attribute(self) -> str: raise NotImplementedError class D(B): __weird_attribute: str def __init__(self, wattribute: str): self.__weird_attribute = wattribute @overrides @readonly_private_property("__weird_attribute") def weird_attribute(self) -> str: pass
关键说明
- 装饰器
readonly_private_property自动生成对应私有属性的getter,避免重复编写return self.__xxx - 保留了类型注解的完整性,静态检查工具可正常识别属性类型
- 完全兼容
overrides装饰器,无需额外忽略注释
方案3:结合dataclasses实现(兼容私有属性规范)
如果想复用dataclasses的简洁性,可通过field的参数控制写入权限,同时将属性设为私有,再暴露只读的property:
from dataclasses import dataclass, field from typing import TypeVar T = TypeVar('T') @dataclass class C: __attribute: T = field(init=True, repr=False) @property def attribute(self) -> T: return self.__attribute # 子类实现(兼容overrides) from abc import ABCMeta, abstractmethod from overrides import overrides class B(metaclass=ABCMeta): @property @abstractmethod def weird_attribute(self) -> T: raise NotImplementedError @dataclass class D(B): __weird_attribute: str = field(init=True, repr=False) @overrides @property def weird_attribute(self) -> str: return self.__weird_attribute
关键说明
dataclasses.field的init=True确保初始化时可设置私有属性,repr=False避免私有属性被自动包含在__repr__中- 依然保留了私有属性的封装性,外部仅能通过只读property访问
- 减少了
__init__方法的样板代码
内容的提问来源于stack exchange,提问作者Anaphory
相关产品推荐
相关产品推荐

