实例属性在 Python3 中可以是不可变的吗

Can instance attribute be immutable in Python3

我想在 __init__ 中设置一个实例属性,然后禁止对其进行任何更改。 Python OOP中有没有这样的机制。更具体地说,我正在使用 Python 3.7.

在您将其否决为重复之前,请注意我说的是 instance attribute 的不变性,而不是 class instance 本身的不变性,就像建议的类似问题标题所询问的那样。

有多种选择,具体取决于您要执行的操作。

1。最终类型

使用 mypy 和 typing_extensions 中的 Final 类型。这仅使用 mypy 进行静态检查,不会在运行时强制执行。

$ pip install typing_extensions

from typing_extensions import Final

class Spam:
    foo: Final[int]
    def __init__(self, foo):
        self.foo = foo

这在撰写本文时应该有效,但 typing_extensions 仍处于试验阶段,可能会发生变化。 Guido 本人一直在研究 mypy,因此它甚至可能最终出现在标准库 typing 模块中。

2。 @属性

使用 属性(可以绕过,但可能不会意外)--

没有setter。您仍然必须将变量存储在某处。可能使用 _ 前缀的名称,或者在闭包中,或者直接在带有 vars 的实例字典上(这将绕过来自 @property 的描述符函数)。

class Spam:
    def __init__(self, foo):
        vars(self)['foo'] = foo
    @property
    def foo(self):
        return vars(self)['foo']

或者使用 setter 检查它是否已经设置,如果是则引发异常。这使您可以在第一次使用普通语法时设置它,但如果您在 __init__.

中设置它可能不值得这么麻烦

3。子类元组

继承自tuple。您必须使用 __new__ 而不是 __init__。您获得了真正的不变性,但这些不再是属性本身,您必须使用整数下标(self[0] 等)访问它们。从 tuple 继承意味着您也获得了所有元组方法,这可能是您不想要的。

如果您仍然想要点访问,您可以尝试创建一个 collections.namedtuple,并从中继承。

from collections import namedtuple

class Spam(namedtuple('Spam', 'foo')):
    def __new__(cls, foo):
        return super().__new__(cls, foo)
    def print_foo(self):
        print(self.foo)

当然,您可以通过这种方式继承更多的方法。

另见,typing.NamedTuple