类型化上下文中只读属性的 Python 模式

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

我们的代码在 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 mypy private-members
1个回答
0
投票

从 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
将在构造函数中需要。

或者跳过数据类并使用下面的示例,如果以下任何一项适用:

  • 正在使用早期版本的 Python(没有适当的数据类支持)
  • 你宁愿不使用数据类
  • 属性的类型应该由传递给构造函数的内容决定
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. 测试

© www.soinside.com 2019 - 2024. All rights reserved.