In Python, how can I make unassignable attributes (like ones marked with `final` in Java)?
Asked Answered
B

12

94

Is there anything in Python that works like the final keyword in Java - i.e., to disallow assigning to a specific attribute of the instances of a class, after those instances have been created? I couldn't find anything like this in the documentation.

I'm creating a snapshot of an object (used for restoration if anything fails); once this backup variable is assigned, it should not be modified -- a final-like feature in Python would be nice for this.

Billhook answered 29/4, 2009 at 14:21 Comment(2)
possible duplicate of Creating constant in PythonEve
@ApurvaSingh While I agree that Python is not suitable for that, there are tuples and frozenset in the standard library and MagicStack/immutables is an implentation of a mutable mapping that will be proposed to be added to the standard library as well.Seigniory
C
73

Having a variable in Java be final basically means that once you assign to a variable, you may not reassign that variable to point to another object. It actually doesn't mean that the object can't be modified. For example, the following Java code works perfectly well:

public final List<String> messages = new LinkedList<String>();

public void addMessage()
{
    messages.add("Hello World!");  // this mutates the messages list
}

but the following wouldn't even compile:

public final List<String> messages = new LinkedList<String>();

public void changeMessages()
{
    messages = new ArrayList<String>();  // can't change a final variable
}

So your question is about whether final exists in Python. It does not.

However, Python does have immutable data structures. For example, while you can mutate a list, you can't mutate a tuple. You can mutate a set but not a frozenset, etc.

My advice would be to just not worry about enforcing non-mutation at the language level and simply concentrate on making sure that you don't write any code which mutates these objects after they're assigned.

Cassandra answered 29/4, 2009 at 14:32 Comment(5)
In Java, a non-primitive variable points to an object. Designating a variable as final means you cannot make the variable point to a different object, however the object that is being pointed to can be mutated.Prisage
There is another use for final in Java, I think that it is the same as static in C++, and that is that the line of code assigning to a final variable is not executed more than once, even if declared inside a function. It allows you to hide the variable from the rest of the code without constantly construct new instances every time the functions is called. In Python you have to use a variable in the global scope or accept the cost of constantly construct new instances. cpp.sh/24gjMoltke
I find it an awful suggestion to simply concentrate on making sure that you don't write any code which mutates these objects after they're assigned. How will you communicate this to other people? Or to your future self? With a comment or docstring? A formal language construct is much less volatile than a comment. Python lacking this is, indeed, worrisome.Wrong
No longer accurate for versions >= 3.8. See the @congusbongus answer below.Plangent
After reading all the "python expert" answers and jokes about declaring constants, it looks like Python is for "single developer project"Pronty
C
76

There is no final equivalent in Python. To create read-only fields of class instances, you can use the property function, or you could do something like this:

class WriteOnceReadWhenever:
    def __setattr__(self, attr, value):
        if hasattr(self, attr):
            raise Exception("Attempting to alter read-only value")
    
        self.__dict__[attr] = value

Also note that while there's @typing.final as of Python 3.8 (as Cerno mentions), that will not actually make values final at runtime.

Carola answered 29/4, 2009 at 14:30 Comment(2)
I always replace self.__dict__[attr] = value with return super(WriteORW, self).__setattr__(attr, value)Lookeron
You may want to update your answer, since there it has been introduced in Python 3.8: docs.python.org/3.8/library/…Henni
C
73

Having a variable in Java be final basically means that once you assign to a variable, you may not reassign that variable to point to another object. It actually doesn't mean that the object can't be modified. For example, the following Java code works perfectly well:

public final List<String> messages = new LinkedList<String>();

public void addMessage()
{
    messages.add("Hello World!");  // this mutates the messages list
}

but the following wouldn't even compile:

public final List<String> messages = new LinkedList<String>();

public void changeMessages()
{
    messages = new ArrayList<String>();  // can't change a final variable
}

So your question is about whether final exists in Python. It does not.

However, Python does have immutable data structures. For example, while you can mutate a list, you can't mutate a tuple. You can mutate a set but not a frozenset, etc.

My advice would be to just not worry about enforcing non-mutation at the language level and simply concentrate on making sure that you don't write any code which mutates these objects after they're assigned.

Cassandra answered 29/4, 2009 at 14:32 Comment(5)
In Java, a non-primitive variable points to an object. Designating a variable as final means you cannot make the variable point to a different object, however the object that is being pointed to can be mutated.Prisage
There is another use for final in Java, I think that it is the same as static in C++, and that is that the line of code assigning to a final variable is not executed more than once, even if declared inside a function. It allows you to hide the variable from the rest of the code without constantly construct new instances every time the functions is called. In Python you have to use a variable in the global scope or accept the cost of constantly construct new instances. cpp.sh/24gjMoltke
I find it an awful suggestion to simply concentrate on making sure that you don't write any code which mutates these objects after they're assigned. How will you communicate this to other people? Or to your future self? With a comment or docstring? A formal language construct is much less volatile than a comment. Python lacking this is, indeed, worrisome.Wrong
No longer accurate for versions >= 3.8. See the @congusbongus answer below.Plangent
After reading all the "python expert" answers and jokes about declaring constants, it looks like Python is for "single developer project"Pronty
N
72

Python 3.8 (via PEP 591) adds Final variables, functions, methods and classes. Here are some ways to use it:

@final Decorator (classes, methods)

from typing import final

@final
class Base:
    # Cannot inherit from Base

class Base:
    @final
    def foo(self):
        # Cannot override foo in subclass

Final annotation

from typing import Final

PI: Final[float] = 3.14159     # Cannot set PI to another value
KM_IN_MILES: Final = 0.621371  # Type annotation is optional

class Foo:
    def __init__(self):
        self.bar: Final = "baz"   # Final instance attributes only allowed in __init__

Please note that like other typing hints, these do not prevent you from overriding the types, but they do help linters or IDEs warn you about incorrect type usage.

Niece answered 14/10, 2019 at 22:20 Comment(5)
I think this answer is misleading as it's easy to get the impression that the final qualifiers will prevent one from reassigning or overriding at runtime, which isn't the case.Seigniory
@Seigniory it's a language feature. IDE's like Visual Studio Code and PyCharm will probably adapt and automatically show a warning if you try to reassign a final instance. This is already the case when you try to use an instance variable with an underscore prefix outside of the class.Bibliomancy
@Bibliomancy Just like a question about private properties should have an answer that explains that nothing is truly private in Python, I think an answer to this question suggesting using the final/Final qualifiers should explain that they do nothing at runtime. Note that I answered this question too.Seigniory
I agree with @Seigniory comment. @typing.final is only used by static type checker. The answer should made it clearer that this decorator is entirely ignored when executing the script. Dynamically created classes will silently ignore this decorator.Amiss
This is better than nothing at least I get a msg in IDE that I am doing something stupidArbitrary
P
11

An assign-once variable is a design issue. You design your application in a way that the variable is set once and once only.

However, if you want run-time checking of your design, you can do it with a wrapper around the object.

class OnePingOnlyPleaseVassily(object):
    def __init__(self):
        self.value = None
    def set(self, value):
        if self.value is not None:
            raise Exception("Already set.")
        self.value = value

someStateMemo = OnePingOnlyPleaseVassily()
someStateMemo.set(aValue) # works
someStateMemo.set(aValue) # fails

That's clunky, but it will detect design problems at run time.

Preordain answered 29/4, 2009 at 14:41 Comment(0)
S
8

As of 2019 and PEP 591, Python has a Final type. It won't be available in the standard library until the release of Python 3.8, but until then you can use it via the typing-extensions library. It won't work as final works in Java though as Python is still a dynamically typed language. But if you use it together with a static type checker like mypy it will give you very similar benefits.

There is also a final decorator that can be applied to mark class methods as final and preventing from being overridden. Again this is only checked at "compile-time", so you'd need to include a static type checker in your workflow.

Seigniory answered 3/9, 2019 at 14:59 Comment(0)
H
7

you can simulate something like that through the descriptor protocol, since it allows to define reading and setting a variable the way you wish.

class Foo(object):

  @property
  def myvar(self):
     # return value here

  @myvar.setter
  def myvar(self, newvalue):
     # do nothing if some condition is met

a = Foo()
print a.myvar
a.myvar = 5 # does nothing if you don't want to
Hunt answered 30/6, 2009 at 10:41 Comment(0)
R
6

There is no such thing. In general, the Python attitude is "if you don't want this modified, just don't modify it". Clients of an API are unlikely to just poke around your undocumented internals anyway.

You could, I suppose, work around this by using tuples or namedtuples for the relevant bits of your model, which are inherently immutable. That still doesn't help with any part of your model that has to be mutable of course.

Romanov answered 29/4, 2009 at 14:29 Comment(1)
Many other classes are immutable, too: strings, ints, longs, floats.Preordain
B
5

Python has no equivalent of "final". It doesn't have "public" and "protected" either, except by naming convention. It's not that "bondage and discipline".

Beekman answered 29/4, 2009 at 14:28 Comment(0)
P
3

http://code.activestate.com/recipes/576527/ defines a freeze function, although it doesn't work perfectly.

I would consider just leaving it mutable though.

Perishing answered 29/4, 2009 at 14:29 Comment(0)
T
2

Python indeed does not have a final type, it does have immutable types such as tuples but that is something else.

Some of the other Answers here make classes full of pseudo final variables and I prefer my class to only have a few Final types, so I suggest using an descriptor to create the final type:

from typing import TypeVar, Generic, Type

T = TypeVar('T')

class FinalProperty(Generic[T]):
    def __init__(self, value: T):
        self.__value = value
    def __get__(self, instance: Type, owner) -> T:
        return self.__value
    def __set__(self, instance: Type, value: T) -> None:
        raise ValueError("Final types can't be set")

If you use this class like so:

class SomeJob:
    FAILED = FinalProperty[str]("Failed")

Then you will not be able to set that variable in any instance of that class. Unfortunately as with the WriteOnceReadWhenever answer you can still set the class variable.

job = SomeJob()
job.FAILED = "Error, this will trigger the ValueError"
SomeJob.FAILED = "However this still works and breaks the protection afterwards"
Tumer answered 10/8, 2018 at 12:20 Comment(0)
C
0

Although this is an old question, I figured I would add yet another potential option: You can also use assert to verify a variable is set to what you originally intended it to be set to – a double checking if you will. Although this is not the same as final in Java, it can be used to create a similar effect:

PI = 3.14
radius = 3

try:
    assert PI == 3.14
    print PI * radius**2
except AssertionError:
    print "Yikes."

As seen above, if PI were for some reason not set to 3.14, an AssertionError would be thrown, so a try/except block would probably be a wise addition. Regardless, it may come in handy depending on your situation.

Clance answered 7/9, 2013 at 5:29 Comment(1)
NB: assert statements are disabled by optimization with -O CLI option, so if + raise.Helmand
M
0

As of Python 3.8, Python does have a keyword "final". However, it is a hint and is not enforced at runtime.

Mirisola answered 23/12, 2022 at 19:19 Comment(3)
It's in the typing module and you use it to annotate attributes - it isn't a keywordPrecancel
Yes, this is true. In this context "keyword" as a very special meaning. I meant it in a wider popular context.,Mirisola
please add an exampleSagitta

© 2022 - 2024 — McMap. All rights reserved.