[Python-ideas] Macro-Powered Enums
Ethan Furman
ethan at stoneleaf.us
Sat Jul 6 04:23:44 CEST 2013
On 07/05/2013 05:56 PM, Haoyi Li wrote:
>
> I know this ship has already sailed with PEP 435, but I've been working on a prototype implementation of enums using
> MacroPy macros <https://github.com/lihaoyi/macropy#enums>.
>
> The goal was to have enums whose methods provide most of the nice capabilities of int enums (auto-generated indexes,
> fast comparison, incrementing, index-arithmetic, find-by-index) and string enums (nice __repr__, find-by-name) but has
> the ability to smoothly scale to full-fledged objects with methods *and* fields, all this is an extremely concise way.
The stdlib Enum and IntEnum offer this already, although not quite as concisely (I haven't looked at your macro code to
see how it handles the missing name lookup, but in standard Python that "feature" invites way too many bugs).
> The gimmick here is that it uses macros to provide a concise syntax to allow you to construct each enum instance with
> whatever constructor parameters you wish, java-enum-style (here <https://github.com/lihaoyi/macropy#complex-enums>).
> This allows a degree of enums-as-objects which i haven't seen in any other library (most don't allow custom fields).
Check out the stdlib Enum -- I think you'll be pleasantly suprised!
> Probably not standard-library worthy, but I thought it was pretty cool.
It is indeed cool.
For comparison, here is your complex Direction Enum in stdlib syntax:
-- 8< ----------------------------------------------------------------------
from enum import Enum
class Direction(Enum):
"""stdlib version of MacroPy complex Directions Enum"""
__order__ = 'North East South West' # for 2.x
North = ("Vertical", ["Northrend"])
East = ("Horizontal", ["Azeroth", "Khaz Modan", "Lordaeron"])
South = ("Vertical", ["Pandaria"])
West = ("Horizontal", ["Kalimdor"])
def __new__(cls, *args):
"__new__ can be omitted if you don't want int-like behavior"
value = len(cls.__members__) + 1
obj = object.__new__(cls)
obj._value = value
return obj
def __init__(self, alignment, continent):
self.alignment = alignment
self.continent = continent
def __int__(self):
"__int__ can be omitted if you don't want int-like behavior"
return self.value
def __index__(self):
return self.value
@property
def opposite(self):
return Direction((self.value + 2) % 4)
def padded_name(self, n):
return ("<" * n) + self.name + (">" * n)
# members
print(Direction.North.alignment) # Vertical
print(Direction.East.continent) # ["Azeroth", "Khaz Modan", "Lordaeron"]
# properties
print(Direction.North.opposite) # Direction.South
# methods
print(Direction.South.padded_name(2)) # <<South>>
# int-like
print(int(Direction.East)) # 1
print(('zero', 'one', 'two', 'three', 'four')[Direction.South]) # 'three'
-- 8< ----------------------------------------------------------------------
Over half the code goes away if you don't need the int-like behavior.
Oh, and the `__order__` is only used in the PyPI version (enum34) so that 2.x enums can still have a "definition" order.
--
~Ethan~
More information about the Python-ideas
mailing list