具有类型提示的 const 方法的 python 等效项

Ore*_*lom 10 python constants type-hinting mypy

我正在尝试找到与 C++const方法等效的 Python 方法。也就是说,禁止更改其类的任何数据成员的方法。

from typing import Final

age: Final = 2
age += 3               # <----- warn about this -- works!

class Person:
    def __init__(self, age: int) -> None:
        self.age = age
    def happy_birthday(self: Final[Person]) -> None:
        self.age += 1  # <----- warn about this -- how?
Run Code Online (Sandbox Code Playgroud)

我从未见过使用过类型提示self,所以它看起来有点奇怪(并且不起作用)

main.py:4: error: Cannot assign to final name "age"
main.py: note: In member "happy_birthday" of class "Person":
main.py:9: error: Final can be only used as an outermost qualifier in a variable annotation  [misc]
        def happy_birthday(self: Final[Person]) -> None:
                                 ^
Run Code Online (Sandbox Code Playgroud)

还有其他方法可以实现这一目标吗?请注意,这些属性仍应允许在其他方法中进行修改。

Qua*_*cha 3

如果它不必是类型提示解决方案,那么您可以执行 PIG208 在评论中建议的操作。即,您可以创建一个装饰器来覆盖该__setattr__方法,以便在修改方法内的任何属性时引发异常。

这也适合按方法附加:

def const_method(func):
    def wrapper(instance):
        class unsettable_class(instance.__class__):
            def __init__(self):
                super().__setattr__("__dict__", instance.__dict__)

            def __setattr__(self, attr, value):
                if hasattr(self, attr):
                    raise AttributeError(f"Trying to set value: {value} on the read-only attribute: {instance.__class__.__name__}.{attr}")
                super().__setattr__(attr, value)

        return func(unsettable_class())
    return wrapper
Run Code Online (Sandbox Code Playgroud)

然后您可以像任何其他装饰器一样使用它:

class Person:
    def __init__(self, age: int) -> None:
        self.age = age

    @const_method
    def happy_birthday(self: "Person") -> None:
        self.age += 1 # throws an error since we are using the custom decorator

    def rebirth(self: "Person") -> None:
        self.age = 0 # will not throw an error since there is no decorator
Run Code Online (Sandbox Code Playgroud)

不幸的是,缺点是此方法不会像类型提示那样在 IDE 中提供语法突出显示。

更新:您可以让自动完成功能以代码仍然是 mypy 正确的方式工作。使用from __future__ import annotations和删除"("Person" -> Person) 就可以了。