Monday, March 02, 2020

True constants in Python - part 1

tl;dr: I'm always trying to see if what everyone "knows to be true" is really true...

In many programming languages, you can define constants via some special declaration. For example, in Java you can apparently write something like:

public static final String CONST_NAME = "Name";

and this will result in a value that cannot be changed.  I wrote "apparently" since I do not program in Java and rely on what other people write.

Everyone "knows" that you cannot do the same in Python.  If you want to define constants, according to Python's PEP 8, what you should do is the following
Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL.
and rely on the fact that everyone will respect this convention.  However, nothing prevents you from redefining the value of these variables later in the same module, or from outside (monkeypatching) when importing the module.

Thus, if I write in a module

TOTAL = 1
# some code
TOTAL = 2

the value of the variable will have changed.

If you are willing to use optional type declaration and either use Python 3.8 with the typing module, or some earlier version of Python but using also the third-party typing-extension package, you can use something like the following:

from typing import Final
TOTAL: Final = 0 

and use a tool like mypy that will check to see if the value of is changed anywhere, reporting if it does so.  However, if you do run such an incorrect program (according to mypy), it will still execute properly, and the value of the "constant" will indeed change.

For people that want something a bit more robust, it is often recommended to use some special object (that could live in a separate module) whose attributes cannot change once assigned. However, this does not prevent one from deleting the value of the "constant object" (either by mistake within the module, or by monkeypatching) and reassign it.

Every Python programmer knows that the situation as described above is the final word on the possibility of creating constants in Python ... or is it?

For example, here's a screen capture of an actual module (called test.py)


Notice how the linter in my editor has flagged an apparent error (using UPPERCASE after deleting it.)  And here's the result of importing this module, and then attempting to change the value of the constant.


Can you think of how I might have done this?  (No photoshop, only the normal Python interpreter used.)

In part 2  , I explain how I have done this and will leave you with a (small) challenge.

No comments: