On Tue, May 25, 2021 at 7:30 AM Joren firstname.lastname@example.org wrote:
typing.Final actually means something like "cannot be reassigned", whereas constant variable means "cannot change". Consider e.g.
class Spam: def __init__(self): self.eggs = 0
@final # method cannot be overridden (which is more related to "reassignment" than to "change") def more(self): self.eggs += 1
spam: Final[Spam] = Spam() spam.more() # This is allowed, but spam changed del spam # Allowed as well
What's the definition of "cannot change"? Which of these counts as a change?
x = (42, ["spam"]) x = "ham" # This?
lst = ["spam"] y = (42, lst) lst = "ham" # This?
r = random.Random() r.randrange(10) # This?
class C: pass class D(C): pass # Is this changing X?
x is a tuple, and we already know that they're immutable. Yet its value changed when something else changed. In y's case, the assignment never even mentions the tuple, yet y's value still changes. The Random instance has internal state which changes every time you ask for a random number. And you can ask a class for all of its subclasses, so the detectable behaviour of it changes when you add a new subclass.
Requiring that a name not be rebound is well-defined and testable. Requiring that an object not change is either trivial (in the case of, say, an integer) or virtually impossible (in the case of most objects).
What would be the advantage of such a declaration?