Enums have been added to Python 3.4 as described in PEP 435. It has also been backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4 on pypi.
For more advanced Enum techniques try the aenum library (2.7, 3.3+, same author as enum34
. Code is not perfectly compatible between py2 and py3, e.g. you'll need __order__
in python 2).
- To use
enum34
, do $ pip install enum34
- To use
aenum
, do $ pip install aenum
Installing enum
(no numbers) will install a completely different and incompatible version.
from enum import Enum # for enum34, or the stdlib version
# from aenum import Enum # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')
Animal.ant # returns <Animal.ant: 1>
Animal['ant'] # returns <Animal.ant: 1> (string lookup)
Animal.ant.name # returns 'ant' (inverse lookup)
or equivalently:
class Animal(Enum):
ant = 1
bee = 2
cat = 3
dog = 4
In earlier versions, one way of accomplishing enums is:
def enum(**enums):
return type('Enum', (), enums)
which is used like so:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
You can also easily support automatic enumeration with something like this:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
and used like so:
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
Support for converting the values back to names can be added this way:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
reverse = dict((value, key) for key, value in enums.iteritems())
enums['reverse_mapping'] = reverse
return type('Enum', (), enums)
This overwrites anything with that name, but it is useful for rendering your enums in output. It will throw a KeyError
if the reverse mapping doesn't exist. With the first example:
>>> Numbers.reverse_mapping['three']
'THREE'
If you are using MyPy another way to express "enums" is with typing.Literal
.
For example:
from typing import Literal #python >=3.8
from typing_extensions import Literal #python 2.7, 3.4-3.7
Animal = Literal['ant', 'bee', 'cat', 'dog']
def hello_animal(animal: Animal):
print(f"hello {animal}")
hello_animal('rock') # error
hello_animal('bee') # passes
Linked to, but not explicitly mentioned here, is exactly when __all__
is used. It is a list of strings defining what symbols in a module will be exported when from <module> import *
is used on the module.
For example, the following code in a foo.py
explicitly exports the symbols bar
and baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
These symbols can then be imported like so:
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
If the __all__
above is commented out, this code will then execute to completion, as the default behaviour of import *
is to import all symbols that do not begin with an underscore, from the given namespace.
Reference: https://docs.python.org/tutorial/modules.html#importing-from-a-package
NOTE: __all__
affects the from <module> import *
behavior only. Members that are not mentioned in __all__
are still accessible from outside the module and can be imported with from <module> import <member>
.
Best Answer
sqlsoup in sqlalchemy gives you the quickest solution in python I think if you want a clear(ish) one liner . Look at the page to see.
It should be something like...