1

I'm attempting to use real constants in Python—values which cannot be modified once defined.

I realize Python has no native support for constants like certain other languages (e.g., final in Java or const in C++), but I want the most Pythonic or safest manner of achieving this behavior.


class Constants:
    PI = 3.14159
    GRAVITY = 9.8

Constants.PI = 3  # This still allows reassignment 

I want something that raises an error or prevents the value from being changed. I’ve also heard of using custom classes or metaclasses, but I’m not sure what the best practice is.

What’s the best way to implement unchangeable constants in Python?

4
  • Perhaps using the property attribute would be a technically plausible way of making read-only constants pythonmorsels.com/making-read-only-attribute Commented Apr 6 at 3:26
  • You could try property and throw errors in setters. Commented Apr 6 at 3:26
  • 4
    Simple question: why? What would "real constants" get you that you can't already get right now with proper code and linting? Commented Apr 6 at 4:46
  • Minor nit pick, but you are asking about named constants. 3.14159 is a constant—you can't assign any other value to it—but it is a literal constant, not a named constant. Commented Apr 6 at 17:27

3 Answers 3

3

To implement unchangeable constants using metaclass, we override the __setattr__ method in the type class (the metaclass). Inside __setattr__, we check if the attribute already exists in the class using cls.__dict__. If it does, we raise an AttributeError.

class ConstantMeta(type):
    def __setattr__(cls, name, value):
        if name in cls.__dict__:
            raise AttributeError(f"Cannot modify '{name}'")
        super().__setattr__(name, value)

class Constants(metaclass=ConstantMeta):
    PI = 3.14159
    GRAVITY = 9.8

Constants.PI = 3 will throw an attribute error

Sign up to request clarification or add additional context in comments.

2 Comments

Although if the concern is "someone could redeclare my values", this just passes the buck. Now we just need to redeclare Constants.
You're absolutely right this approach mainly protects against changing values inside the class. If someone really wants to, they can just override or reassign the class itself.
1

You're correct—Python natively supports neither true constants like C, C++, nor Java. There is no const keyword, and even the hack of creating variables in all caps (PI = 3.14) is completely dependent on developer discipline.

Although workarounds such as metaclasses or special classes can be used to mimic immutability, none are able to prevent reassignment at the module level, or prevent an astute user from simply overwriting your values.

Therefore, I created something that does.

Introducing setconstant: Immutability in Python Constants

Python is not immutable, and that can have unintended value mutations—particularly in big codebases. So I made setconstant, a light Python package allowing you to define true constants that cannot be mutated after declaration.

How It Works

import setconstant
setconstant.const_i("PI", 3)
setconstant.const_f("GRAVITY", 9.81)
setconstant.const_s("APP_NAME", "CodeHaven")
print(setconstant.get_constant("PI"))       # Output: 3
print(setconstant.get_constant("GRAVITY"))  # Output: 9.81
print(setconstant.get_constant("APP_NAME")) # Output: CodeHaven
# Try to change the value

Once declared, constants are locked. Any attempt to overwrite them throws a ValueError, protecting your code from bugs or unintended logic changes.

Why Not Just Use a Class or Metaclass?

class Constants:
  PI = 3.14
  def __setattr__(self, name, value):
      raise AttributeError("Cannot reassign constants")

Or even using metaclasses like:

class ConstantMeta(type):
  def __setattr__(cls, name, value):
     if name in cls.__dict__:
        raise AttributeError(f"Cannot modify '{name}'")
     super().__setattr__(name, value)

But these solutions have their limitations:

They can be circumvented with sufficient effort.

They are not intuitive to beginners.

They need boilerplate or more advanced Python functionality such as metaclasses.

setconstant circumvents that. It's simple, strict, and does what constants are supposed to do.

Installation

pip install setconstant

Feedback is always welcome!

Anuraj R, Creator of setconstant

Comments

0

Strictly speaking, this is impossible. There are tricks to make some attributes immutable, but the user can always override another level up. In the most extreme case, they can override on the module-level - there's no way to prevent that since PEP 726 was rejected. (Specifically to prevent module-level constants, I might add.)

It's also probably not necessary. If you name the variable in all caps, no self-respecting Python developer will ever override it unless they have a really good reason.

If you insist on doing the best you can, you can use metaclasses as in Felix's answer. Alternatively, you can do something like this:

class Constants:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    def __setattr__(self, name, value):
        if name in dir(self):
            raise AttributeError(f"cannot assign to constant '{name}'")
        super().__setattr__(name, value)

This lets you create new constant groups on the fly, as well as add new constants to groups you already have. (If you don't like the latter behavior, replace the last line in __setattr__ with another raise statement.)

>>> c = Constants(PI=3.14159, GRAVITY=9.8)
>>> c.PI
3.14159
>>> c.PI = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/anerdw/stackoverflow/constants.py", line 7, in __setattr__
    raise AttributeError(f"cannot assign to constant '{name}'")
AttributeError: cannot assign to constant 'PI'
>>> c.GRAVITY
9.8
>>> c.GRAVITY = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/anerdw/stackoverflow/constants.py", line 7, in __setattr__
    raise AttributeError(f"cannot assign to constant '{name}'")
AttributeError: cannot assign to constant 'GRAVITY'
>>> c.X = 0
>>> c.X
0
>>> c.X = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/anerdw/stackoverflow/constants.py", line 7, in __setattr__
    raise AttributeError(f"cannot assign to constant '{name}'")
AttributeError: cannot assign to constant 'X'

Note that this lets users delete constants with the del keyword. You can override __delattr__ as well if you care about that. Or you can do what I would do, which is not bother with any of this at all.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.