Introduction
"Constants" are variables whose values are intended to remain unchanged throughout the program's execution. Unlike some other programming languages, Python does not have a built-in mechanism to enforce immutability for constants. However, there are approaches that can be used to indicate that a variable is a constant.
One approach is to rely on a widely adopted naming convention. A second approach is to use typing.Final. A third approach is to use slots. The rest of this blog will address each approarch in turn.
Approach 1: Naming Convention Using All Capital Letters
The disadvantage is that it requires developers to remember to follow the naming convention and reviewers to remember to check for the naming convention. On top of that, there can be no static or runtime checking.
The advantage is that no additional code besides declaring the "constnat" is needed.
Approach 2: Use typing.Final
Let's demonstrate how typing.Final can detect "constants" being modified via a code snippet. Create a file with the name constants.py with the following contents:
from typing import Final CONSTANT_A: Final = "A" CONSTANT_B: Final = "B" CONSTANT_A = "Z" # Line 6
When run "mypy constants.py", the following output is generated:
constants.py:6: error: Cannot assign to final name "CONSTANT_A" [misc] Found 1 error in 1 file (checked 1 source file)
A disadvantage is that it produces no runtime errors.
>>> from typing import Final >>> CONSTANT_A: Final = "A" >>> CONSTANT_B: Final = "B" >>> CONSTANT_A = "Z" # Line 6 >>>
However, if use a static type checker, the code won't pass basic QA and consequently it will never make it to production.
The advantage of this approach is that it gurantees that "constants" cannot be modified.
Approach 3: Slots
Let's demonstrate how slots can be used to create constants via a working code snippet
>>> class ConstantsNamespace: ... __slots__ = () ... SOME_CONSTANT = "Hi" ... >>>
If try to modify the constant during run time, an error will be generated
>>> constants = ConstantsNamespace() >>> constants.SOME_CONSTANT = "Bye" Traceback (most recent call last): File "<python-input-8>", line 1, in <module> constants.SOME_CONSTANT = "Bye" ^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'ConstantsNamespace' object attribute 'SOME_CONSTANT' is read-only >>>
The advantage is that run time errors are generated.
The disadvantage is that the "constants" can be modified because they are class variables.
>>> constants.SOME_CONSTANT 'Hi' >>> ConstantsNamespace.SOME_CONSTANT = "bye" >>> constants.SOME_CONSTANT 'bye' >>>
If you want a quick tutorial on slots, refer to the section "Lightweight Classes With .__slots__" in the article "Python Classes: The Power of Object-Oriented Programming" by Leodanis Pozo Ramos at Real Python.
Summary
We have discussed 3 different approaches. I personally like approach 2 where typing.Final is used. It is simple to implement and it can be used to gurantee that a "constant variable" is never changed.
If you are interested in an in depth presentation about Python constants, refer to the article "Python Constants: Improve Your Code's Maintainability" by Leodanis Pozo Ramos from Real Python.
No comments:
Post a Comment