git.net

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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 
meanings, eg

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
<Frequency.HOURLY: 1>
 >>> 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
3

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
False


However, ignoring the value completely, setting

 >>> argument = Frequency.DAILY

and then posting argument into the API, the parameter assumes 
semantic-meaning:

 >>> 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!


Questions:

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?


WebRefs:
By gum: https://www.phrases.org.uk/meanings/82225.html
Mike Driscoll: 
https://www.blog.pythonlibrary.org/2018/03/20/python-3-an-intro-to-enumerations/
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/

-- 
Regards,
=dn

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, 
it's me!