There are multiple options depending on exactly what you are trying to do.
1. Final type
Use mypy and the Final type from typing_extensions. This is only checked statically with mypy, not enforced at runtime.
$ pip install typing_extensions
from typing_extensions import Final
class Spam:
foo: Final[int]
def __init__(self, foo):
self.foo = foo
This should work as of this writing, but the typing_extensions are still kind of experimental, and may change. Guido himself has been working on mypy, so it might even end up in the standard library typing module.
2. @property
Use a property (which can be circumvented, but probably not on accident) either--
Without a setter. You still have to store the variable somewhere. Maybe with a _-prefixed name, or in a closure, or directly on the instance dict with vars (which will bypass the descriptor function from @property).
class Spam:
def __init__(self, foo):
vars(self)['foo'] = foo
@property
def foo(self):
return vars(self)['foo']
Or with a setter that checks if it's already set, and raises an exception if it is. This lets you set it the first time with the normal syntax, but it's probably not worth the bother if you're setting it in __init__.
3. subclass tuple
Inherit from tuple. You'll have to use __new__ instead of __init__. You get real immutability, but these are no longer attributes per se and you have to access them with integer subscripts (self[0] etc.). Inheriting from tuple means you get all the tuple methods too, which you may not want.
If you still want the dot access, you can try creating a collections.namedtuple instead, and inherit from that.
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)
Of course, you inherit even more methods this way.
See also, typing.NamedTuple.