/Demo/newmetaclasses/Enum.py

http://unladen-swallow.googlecode.com/ · Python · 177 lines · 136 code · 8 blank · 33 comment · 14 complexity · d98b2efd81d2845766343bba7465bac2 MD5 · raw file

  1. """Enumeration metaclass."""
  2. class EnumMetaclass(type):
  3. """Metaclass for enumeration.
  4. To define your own enumeration, do something like
  5. class Color(Enum):
  6. red = 1
  7. green = 2
  8. blue = 3
  9. Now, Color.red, Color.green and Color.blue behave totally
  10. different: they are enumerated values, not integers.
  11. Enumerations cannot be instantiated; however they can be
  12. subclassed.
  13. """
  14. def __init__(cls, name, bases, dict):
  15. super(EnumMetaclass, cls).__init__(name, bases, dict)
  16. cls._members = []
  17. for attr in dict.keys():
  18. if not (attr.startswith('__') and attr.endswith('__')):
  19. enumval = EnumInstance(name, attr, dict[attr])
  20. setattr(cls, attr, enumval)
  21. cls._members.append(attr)
  22. def __getattr__(cls, name):
  23. if name == "__members__":
  24. return cls._members
  25. raise AttributeError, name
  26. def __repr__(cls):
  27. s1 = s2 = ""
  28. enumbases = [base.__name__ for base in cls.__bases__
  29. if isinstance(base, EnumMetaclass) and not base is Enum]
  30. if enumbases:
  31. s1 = "(%s)" % ", ".join(enumbases)
  32. enumvalues = ["%s: %d" % (val, getattr(cls, val))
  33. for val in cls._members]
  34. if enumvalues:
  35. s2 = ": {%s}" % ", ".join(enumvalues)
  36. return "%s%s%s" % (cls.__name__, s1, s2)
  37. class FullEnumMetaclass(EnumMetaclass):
  38. """Metaclass for full enumerations.
  39. A full enumeration displays all the values defined in base classes.
  40. """
  41. def __init__(cls, name, bases, dict):
  42. super(FullEnumMetaclass, cls).__init__(name, bases, dict)
  43. for obj in cls.__mro__:
  44. if isinstance(obj, EnumMetaclass):
  45. for attr in obj._members:
  46. # XXX inefficient
  47. if not attr in cls._members:
  48. cls._members.append(attr)
  49. class EnumInstance(int):
  50. """Class to represent an enumeration value.
  51. EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves
  52. like the integer 12 when compared, but doesn't support arithmetic.
  53. XXX Should it record the actual enumeration rather than just its
  54. name?
  55. """
  56. def __new__(cls, classname, enumname, value):
  57. return int.__new__(cls, value)
  58. def __init__(self, classname, enumname, value):
  59. self.__classname = classname
  60. self.__enumname = enumname
  61. def __repr__(self):
  62. return "EnumInstance(%s, %s, %d)" % (self.__classname, self.__enumname,
  63. self)
  64. def __str__(self):
  65. return "%s.%s" % (self.__classname, self.__enumname)
  66. class Enum:
  67. __metaclass__ = EnumMetaclass
  68. class FullEnum:
  69. __metaclass__ = FullEnumMetaclass
  70. def _test():
  71. class Color(Enum):
  72. red = 1
  73. green = 2
  74. blue = 3
  75. print Color.red
  76. print repr(Color.red)
  77. print Color.red == Color.red
  78. print Color.red == Color.blue
  79. print Color.red == 1
  80. print Color.red == 2
  81. class ExtendedColor(Color):
  82. white = 0
  83. orange = 4
  84. yellow = 5
  85. purple = 6
  86. black = 7
  87. print ExtendedColor.orange
  88. print ExtendedColor.red
  89. print Color.red == ExtendedColor.red
  90. class OtherColor(Enum):
  91. white = 4
  92. blue = 5
  93. class MergedColor(Color, OtherColor):
  94. pass
  95. print MergedColor.red
  96. print MergedColor.white
  97. print Color
  98. print ExtendedColor
  99. print OtherColor
  100. print MergedColor
  101. def _test2():
  102. class Color(FullEnum):
  103. red = 1
  104. green = 2
  105. blue = 3
  106. print Color.red
  107. print repr(Color.red)
  108. print Color.red == Color.red
  109. print Color.red == Color.blue
  110. print Color.red == 1
  111. print Color.red == 2
  112. class ExtendedColor(Color):
  113. white = 0
  114. orange = 4
  115. yellow = 5
  116. purple = 6
  117. black = 7
  118. print ExtendedColor.orange
  119. print ExtendedColor.red
  120. print Color.red == ExtendedColor.red
  121. class OtherColor(FullEnum):
  122. white = 4
  123. blue = 5
  124. class MergedColor(Color, OtherColor):
  125. pass
  126. print MergedColor.red
  127. print MergedColor.white
  128. print Color
  129. print ExtendedColor
  130. print OtherColor
  131. print MergedColor
  132. if __name__ == '__main__':
  133. _test()
  134. _test2()