pythonenumsdocumentation

How do I put docstrings on Enums?


Python 3.4 has a new enum module and Enum data type.

Since Enum members support docstrings, as pretty much all python objects do, I would like to set them. Is there an easy way to do that?


Solution

  • Yes there is, and it's my favorite recipe so far. As a bonus, one does not have to specify the integer value either. Here's an example:

    class AddressSegment(AutoEnum):
        misc = "not currently tracked"
        ordinal = "N S E W NE NW SE SW"
        secondary = "apt bldg floor etc"
        street = "st ave blvd etc"
    

    You might ask why I don't just have "N S E W NE NW SE SW" be the value of ordinal? Because when I get its repr seeing <AddressSegment.ordinal: 'N S E W NE NW SE SW'> gets a bit clunky, but having that information readily available in the docstring is a good compromise.

    Here's the recipe for the Enum:

    class AutoEnum(enum.Enum):
        """
        Automatically numbers enum members starting from 1.
    
        Includes support for a custom docstring per member.
        """
        #
        def __new__(cls, *args):
            """Ignores arguments (will be handled in __init__."""
            value = len(cls) + 1
            obj = object.__new__(cls)
            obj._value_ = value
            return obj
        #
        def __init__(self, *args):
            """Can handle 0 or 1 argument; more requires a custom __init__.
    
            0  = auto-number w/o docstring
            1  = auto-number w/ docstring
            2+ = needs custom __init__
    
            """
            if len(args) == 1 and isinstance(args[0], (str, unicode)):
                self.__doc__ = args[0]
            elif args:
                raise TypeError('%s not dealt with -- need custom __init__' % (args,))
    

    And in use:

    >>> list(AddressSegment)
    [<AddressSegment.ordinal: 1>, <AddressSegment.secondary: 2>, <AddressSegment.misc: 3>, <AddressSegment.street: 4>]
    
    >>> AddressSegment.secondary
    <AddressSegment.secondary: 2>
    
    >>> AddressSegment.secondary.__doc__
    'apt bldg floor etc'
    

    The reason I handle the arguments in __init__ instead of in __new__ is to make subclassing AutoEnum easier should I want to extend it further.