Friday Finking: Enum by gum
When, why, and how do you employ Python/PSL enums?
TLDR? Fair enough! Much of this text recounts a voyage of discovery.
More specific questions appear at the bottom...
Last century, when I learned to program (in-between working part-time at
the dinosaur farm) neither FORTRAN II/IV nor COBOL-68 offered enums.
Interestingly, whilst most of our team-members learned about them
(ComSc) or had been trained to use them in xyz-language, few remember
any mention of them in Python courses/books/tutorials - nor could anyone
recall (local) usage.
This fascinated me (small things amuse small minds) because I had been
called-in to settle a 'disagreement' where users thought they should
enter data 'one way' into an interface/API, whereas the design had
directed it otherwise. Was it a user issue, a design problem, an
inflexible interface, poor documentation, weak design, ...?
This in-turn led me to consider: can we both illustrate the interface
(not just in docs) AND ease the user-side of the interface AND/OR
improve the error-checking*/feedback? (and do it cheaply~$0, and take no
time at all, and leap tall-buildings at a single...)
* perhaps by controlling what control-data (cf statistics/source-data)
may be input.
Accordingly, as 'they' say, there is a "first time for everything", and
I starting playing with enums...
The PSL manual is somewhat sparse. The examples are weak and almost
solely introspective: if I have an enum, I can ask it what it is. Should
that sum-up the total use of enums?
By-and-large, most web-refs seem weak and pointless repetitions of the
PSL docs (perhaps only the parts that the author actually understood?);
reciting uses such as defining the suits in a pack/deck of cards, but
not describing how the enum might actually be used in (real) code.
Negative motivations resulted from the first part of Mike 'the Mouse'
Driscoll's comment: "Whilst I don?t think the enum module is really
necessary for Python, it is a neat tool...", but interest was recovered
with the closing observation. "Neat" for what exactly?
With the stated-intention that enums would be back-ported/re-factored
into the PSL, the PEP(s) dance-around the idea that existing code may
run off integer parameters which are understood to hold particular
0 => hourly,
1 => daily,
2 => weekly, etc
In the normal course of Python there is nothing to stop one from setting
such an parameter to 10, which has no actual meaning within the API (~my
user-issue, above). Nor is the user (or the original coder) prevented
from using the parameter for a purpose other than its temporal intention!
Enter the enum: applying semantic meaning:
>>> Frequency = enum.Enum( "Frequency", "HOURLY DAILY WEEKLY" )
Here, whilst there is an int value, it can't be used as such:
>>> Frequency.HOURLY + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'Frequency' and 'int'
Unless (are we "all adults here"?):
>>> Frequency.HOURLY.value + 2
OK, so it can be used, mis-used, and abused...
Plus, be aware:
>>> 1 in Frequency
__main__:1: DeprecationWarning: using non-Enums in containment checks
will raise TypeError in Python 3.8
However, ignoring the value completely, setting
>>> argument = Frequency.DAILY
and then posting argument into the API, the parameter assumes
>>> if parameter is Frequency.DAILY:
... # multiply by 24 to use hours as common metric
So, in my thinking about interfaces: if the first code-line is on the
calling-side of an API and the latter inside our 'black-box', that
code-snippet seemed to fit my spec and illustrate an actual and useful
purpose for enums. (OK, I might be slow, but I'll get there...) Agreed?
Thus, alongside the API we need to publish the enum, and 'advertise' it
as a "helper". A good choice of name (in the users' view(s)) may also
avoid mis-communications and other errors... Do you agree?
The earlier PEP talked of "enumerated values hiding as much as possible
about their implementation" which also makes sense in this application,
because I don't care about any underlying value for Frequency.DAILY,
only that it be checked/compared - and that if someone enters 0, 1, or
something of their own devising (or even Fr:"QUOTIDIEN" (== En:"DAILY"))
it is not (in this case) regarded as 'equal' or otherwise equivalent!
This point is picked-up in PEP 435: "an enumeration ensures that such
values are distinct from any others including, importantly, values
within other enumerations". Hence (in the snippet, above) the "is", ie
looking at "identity" rather than value!
Have I made proper sense of this? (please don't laugh too much)
Is the above 'interfacing' an appropriate use of enum-s; or is it really
'overkill' or posturing?
Do you use enum-s very often?
What other 'pattern' of application do you employ?
Python v3.6+ brings Flag-enums into the game. These seem better for
interface use, as described. Do you prefer them to the 'plain-vanilla'
enum, and for what reason?
By gum: https://www.phrases.org.uk/meanings/82225.html
PSL enums: https://docs.python.org/3.7/library/enum.html
PEP 354 (superseded): https://www.python.org/dev/peps/pep-0354/
PEP 435 (accepted): https://www.python.org/dev/peps/pep-0435/
PS: life has been 'exciting' here. Some thoughtless person cut-through
our local fiber back-haul, and even since the service was restored,
there have been throughput problems, connection issues, ... Apologies if
I haven't caught-up with you amongst the massive backlog - it's not you,