[HN Gopher] Using Enumerated Types in Python
       ___________________________________________________________________
        
       Using Enumerated Types in Python
        
       Author : kaunta
       Score  : 52 points
       Date   : 2020-06-06 16:56 UTC (6 hours ago)
        
 (HTM) web link (johnlekberg.com)
 (TXT) w3m dump (johnlekberg.com)
        
       | NicoJuicy wrote:
       | Recently I started to use enumeration classes instead of enums (
       | where it fits)
       | 
       | https://docs.microsoft.com/en-us/dotnet/architecture/microse...
       | 
       | If you develop in c#, I would definitely recommend trying it out
       | 
       | A better example can be found here actually:
       | https://lostechies.com/jimmybogard/2008/08/12/enumeration-cl...
        
         | nerdponx wrote:
         | What's the basic idea here? That instead of ints or strings,
         | your enum values themselves are instances of custom classes
         | with whatever behavior you want?
        
           | NicoJuicy wrote:
           | If I would describe the use-case:
           | 
           | If you use switches with enums for functionality, this is a
           | better way ( personal opinion). Since Enums quickly need more
           | than 1 switch and this could wrap that behaviour
           | 
           | Your description is also pretty good
        
       | ramraj07 wrote:
       | Have constantly struggled over how to deal with this problem. The
       | biggest need for enums has been function arguments which can be
       | one of a few values.
       | 
       | In the end, the Literal class from the typing library seems like
       | the simplest, least wordy solution. Pycharm and mypy capture any
       | mistakes you make which seem more than sufficient for my use
       | cases.
        
         | nerdponx wrote:
         | Why not use Enum anyway, which works fine with MyPy? I don't
         | mind the verbosity, it's a one-time commitment to what, 10
         | extra lines of code?
        
           | ramraj07 wrote:
           | Issue is I'll now need two imports everytime I call this
           | method, which becomes mildly annoying. I was doing this, but
           | a lot of times the Literal type seems more than sufficient.
           | It also seems to play real nice with pydantic so it's an
           | added win!
        
       | elcomet wrote:
       | Enums are best when combined with a language that does pattern
       | matching and that can warn you if you forgot a value in your case
       | statement. Python lacks this, so there is no spectacular benefit
       | of using enums over just a list of strings.
        
         | zem wrote:
         | there are several benefits to using named constants over
         | literal strings, and enums give you namespaced named constants
        
           | nerdponx wrote:
           | Not only that, but both static type-checking (with
           | appropriate tooling) and convenient runtime membership
           | checks.
        
         | zo1 wrote:
         | The article is incredibly misleading. Python Enums are way more
         | powerful than what is showcased there.
         | 
         | First, rudimentary/basic enums are as simple as instance
         | variables on a class and allow super-helpful IDE "auto-
         | completion" that doesn't necessitate the use of magic-strings
         | to at least "set" the enum. I've been using this ridiculously
         | simple trick since 2.5 ten years ago. Magic-strings are bad,
         | and even worse when you have a disparate code-base in a
         | dynamically typed language. Example:                   class
         | MyEnum(object):             Pass = 1             Fail = 2
         | my_value = MyEnum. <-- at this point, the IDE gives you code-
         | completion.
         | 
         | Second, real Enums added as a language-feature along with type-
         | hinting are almost 100% on-par with statically typed languages.
         | See:                   class MyEnum(Enum):             Pass = 1
         | Fail = 2              def my_func(did_they_pass: MyEnum):
         | if did_they_pass.Something:   # IDE / type-checker will go nuts
         | here.                 print("Something")
         | 
         | Edit: Forgot the main point. Switch/case uses are only a small
         | sub-set of the uses of Enums. So even basic/crappy enums are
         | better than magic strings in a dynamically-typed language.
         | Also, please don't use "list of strings" for enums in Python,
         | there are much better ways to do that sort of thing that
         | preclude unnecessary bugs popping up.
        
           | andrepd wrote:
           | _> Second, real Enums added as a language-feature along with
           | type-hinting are almost 100% on-par with statically typed
           | languages_
           | 
           | This is conclusive proof that you're not actually aware of
           | the capabilities of enums and sum types in "statically typed
           | languages". Where's pattern matching? Exhaustivity checking?
           | Sum types? GADTs?
        
           | throwgeorge wrote:
           | >real Enums added as a language-feature along with type-
           | hinting are almost 100% on-par with statically typed
           | languages
           | 
           | sorry i don't understand this. i'm not trying to be pedantic
           | but: statically typed (and type-checked) language will fail
           | to compile ... not just warn you. that's not a 1% difference
           | that's the entire difference (because lots of people will
           | miss or flat out ignore ide hints).
        
           | BiteCode_dev wrote:
           | So, you don't use open("w"), do you ? You use the non magic
           | open(os.O_WRONLY) of course.
           | 
           | Because magic strings are bad.
        
             | comfydragon wrote:
             | Python 3.8 has typing.Literal for this.
        
             | MikeTheGreat wrote:
             | I will now - thanks for the tip! :)
             | 
             | And, moreover, this will inspire me to ask "How many other
             | traditional magic numbers/strings/etc have I gotten used to
             | that might be defined more readably?" strcmp, I'm looking
             | at you!
             | 
             | I know your post was sarcastic/snarky but it was genuinely
             | helpful
        
               | 1wd wrote:
               | f.tell(offset, os.SEEK_CUR) over f.tell(offset, 1)
               | 
               | https://docs.python.org/3/library/os.html#os.SEEK_CUR
               | 
               | I wish the docs on f.tell at least mentioned the option
               | 
               | https://docs.python.org/3/tutorial/inputoutput.html#metho
               | ds-...
        
             | 1wd wrote:
             | You would have to use os.open
        
         | silviogutierrez wrote:
         | Agreed on pattern matching, though mypy can help a lot here
         | with exhaustive checks at the very least. They do work on enums
         | and discriminated unions. They should document it:
         | 
         | https://github.com/python/mypy/issues/6366
         | 
         | https://github.com/python/mypy/issues/5818
        
         | crimsonalucard1 wrote:
         | There's a benefit in the cardinality during type checking when
         | you use an external type checker. Strings have infinite
         | possibilities an enum type is strictly defined.
         | 
         | Also I'm sure with reflection you can actually implement a
         | simple enum pattern matching function on an enum that is
         | exhaustive and throws a runtime error immediately if every
         | possible enum case is not covered.
        
           | zo1 wrote:
           | I'm not following this thread at all. What "exhaustive
           | search" are you guys referring to? In a switch/case block?
           | Python doesn't have that, unless I missed it while living
           | under a rock.
           | 
           | I.e. if you're working with python Enums, you type-check for
           | that Enum, and then the IDE does it all for you. If you want
           | to "check" for a specific enum, then you cast to it and the
           | runtime Enum implementation does that checking for you by
           | throwing an error if you try create that Enum with an invalid
           | string value. And if you've got a raw string, before it goes
           | too-far into your code base, you cast it into the specific
           | Enum type, and when serializing you serialize it to the
           | string value. Easy-peasy.
        
             | tobywf wrote:
             | AFAIK, they're talking about languages like Rust, or C/C++
             | with `-Wswitch` (also implied by `-Werror`, which will warn
             | you if you haven't handled all enum cases in a match/switch
             | statement.
             | 
             | as someone else in this thread pointed out [0], it's
             | possible with MyPy when using if/elif/else, or if you did
             | it with a dictionary lookup, you could write a very simple
             | test that checks that the set of enum values is equal to
             | the dictionary key members. I've done this before, it works
             | well [1].
             | 
             | [0] https://news.ycombinator.com/item?id=23441023 [1]
             | https://github.com/aws-cloudformation/cloudformation-
             | cli/blo...
        
             | icegreentea2 wrote:
             | In some typed/compiled languages, a substantial benefit of
             | using enums is that it provides some way to automatically
             | determine if you've handled every eventuality. That's super
             | not a part of normal Python as you've mentioned.
             | 
             | The second part of crimsonalucard1's post was referring to
             | somehow hacking in that type of functionality into Python.
             | 
             | I think you could somehow do that with nested context
             | managers - something like:                 with
             | EnumChecker(e) as checker:         with
             | checker(enum_type.A):           # do thing         with
             | checker(enum_type.B):           # do thing         ...
             | 
             | Where `e` is an instance of `enum_type`, and the inner
             | context managers acting as if/case statements, then you
             | could on any single execution of the code block (so you
             | only need a single test case) ensure that you covered all
             | states.
        
             | crimsonalucard1 wrote:
             | imagine an enum called bool with two values: True and
             | False.                 switch(value: bool):          case
             | True:              // do something          case False:
             | // do something else       end
             | 
             | The above code is exhaustive in the sense that the logic
             | covers all possible values of the enum type. The
             | "exhaustive matching" we're talking about is similar to a
             | pre runtime type check.
             | 
             | So imagine where someone writes this code:
             | switch(value):          case True:              // do
             | something       end
             | 
             | The user did not handle the False case in his logic.
             | Exhaustive pattern matching will catch this before the
             | program runs and throw an error similar to a syntax or type
             | check error. It will say that your switch function needs to
             | handle all possible cases of bool. It needs to handle True
             | AND False not just True.
             | 
             | The power of this basically allows you to encode safety of
             | if else logic into a type and pass that safety throughout
             | your program. It's pretty incredible, so much so that
             | whenever this feature is part of a language I have stopped
             | using if else statements almost completely.
             | 
             | Keep in mind the power of pattern matching extends beyond
             | just exhaustive enums. An int is a form of enum as well:
             | switch(value: int):             case 0:                //
             | do something             case >0:                // do
             | something else          end
             | 
             | with powerful pattern matching features the compiler should
             | immediately let you know that your switch statement did not
             | handle the case "<0".
        
         | oflannabhra wrote:
         | Agreed. I've currently switched from Swift to Python and keep
         | reaching for Swifts expressive enums and awesome pattern
         | matching.
        
       ___________________________________________________________________
       (page generated 2020-06-06 23:00 UTC)