Using Enumerated Types in Python
johnlekberg.com
johnlekberg.com
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.
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.
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...
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. 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".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.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).
Because magic strings are bad.
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
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#methods-...
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?
Enums represent a discrete set of constant values. They are extremely helpful to communicate to developers what to expect from a component. Lists of strings do not come near to offer the same functionality.
There are also good reasons why Python does not have switch/case statements, which are generally an anti-pattern; they work fine for a quick fix, but what they do is better solved with proper polymorphism.
Clown = circus.Actor(“Clown”)
Why is it needed, instead of just creating an anonymous class? Clown = circus.Actor()
Or allowing the class factory to manipulate the current namespace? circus.Actor(“Clown”)
bozo = Clown()
I feel like I am perilously close to answering my own question, but not close enough. I’m sure there’s a good reason, but I don’t know enough of the Python internals to figure it out.I think the question should be: why do you want an anonymous class? If it's sufficiently ad-hoc that it doesn't warrant a name, you would typically use a dict.
> Or allowing the class factory to manipulate the current namespace?
I think that would just be considered too surprising and non-standard behaviour (other Python functions don't get to modify the scope they're called from).
But you’re right. It’s hardly cumbersome to create classes that simply exist to contain data in a sightly more structured way than a dict.
Doesn't a lot of that improvement come from the fact that they have a name, though? I guess you could argue for an "UnnamedTuple" which only enforces tuple size and member names. But if you're able to specify the member names, it feels like the thing being represented is concrete enough that you should be able to come up with a name for it, and that having a name would be beneficial.
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...
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
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.
If you define your Enum subclass in the same file, you use one import for enum.Enu. If you eschew enums and use Literal, you use one import for typing.Literal.
You also probably want dataclasses [1] instead of named tuples.
https://github.com/klaviyo/matrix_enum
My colleague and I started making use of that pattern last year (before open sourcing the package) and found it very useful.
Can you give an example of how you use enums and named tuples in the same way? That just doesn’t seem to make a lot of sense to me, but maybe I’m missing something.