mypy
mypy copied to clipboard
Overloading an abstract attribute with a Final one
In the following code it would be useful to mark toto as final to make sure no code modifies it by error. However this triggers Cannot override writable attribute "toto" with a final one
from typing import Final
class Base:
toto: str = NotImplemented
def access_toto(self) -> str:
return self.toto
class CmdA(Base):
toto: Final[str] = "toto"
In fact I tend to agree with that error, in that what I'd really like to do is to annotate toto as "abstract final", which is not possible either as the use of toto: Final[str] triggers Final name must be initialized with a value, and toto: Final[str] = NotImplemented still gets the
Cannot override final attribute "toto" (previously declared in base class "Base") the previous attempt also showed.
- Mypy version used: 1.1.1, 0.812
- Python version used: 3.9.2
If it's final, it means it cannot be changed
class Base:
toto: str = NotImplemented
def change_toto(self) -> None:
self.toto = "spam"
class CmdA(Base):
toto: Final[str] = "toto"
c = CmdA()
c.change_toto() # changes `c.toto` even though it's final :(
What are you trying to achieve with Final?
@ikonst you're right, base.toto should not be mutable, this was not the best of those attempts :)
What I'd like is to ensure all concrete subclasses of Base have final strings for their .toto.
This doesn't require all subclasses to have final strings (I'm not sure how you could ensure that) but you can make your original example work with a readonly property!
from typing import Final
from abc import ABC, abstractmethod
class Base(ABC):
@property
@abstractmethod
def toto(self) -> str:
...
def access_toto(self) -> str:
return self.toto
class CmdA(Base):
toto: Final[str] = "toto"
Should we close this, or do we consider this a usability problem with the current error "Cannot override writable attribute "{name}" with a final one'"?
For example, do we need treatment similar to this? https://github.com/python/mypy/blob/267d37685a35e25eb985d56b6c6881ba574fcc7f/mypy/messages.py#L1194-L1205
My original point was more about:
- specifying an attribute in parent class without implying it would be writable, so that a derived class could refine it as being final
- going one step further and specifying that this attribute should not be writable
@A5rocks yes this answers point 1, though I prefer avoiding ABC when stuff like ABC.register() is not needed (it's a PITA when you start to have other metaclasses), so I prefer this slightly modified version:
from typing import Final
class Base:
@property
def toto(self) -> str:
raise NotImplementedError()
def access_toto(self) -> str:
return self.toto
class CmdA(Base):
toto: Final[str] = "toto"
For point 2 I still don't have any idea. I'd think the final annotation in superclass without an assignment could have a use here, it would just declare an "abstract attribute" -- assigning NotImplemented could likely not be interpreted in another way, but going that way creates a special case which the first option avoids. Would there be any problem going that way ?
@ikonst: yes I'd tend to think similar treatment would help, but it's not the whole of this ticket ;)
Hm I just re-read this and I think typing-wise a property setter that has a NoReturn return type might work for point 2, but I'm not sure if mypy supports that and I'm on mobile so can't test.