/Demo/metaclasses/Enum.py

http://unladen-swallow.googlecode.com/ · Python · 169 lines · 117 code · 11 blank · 41 comment · 2 complexity · 6de8ce8b7bef0e6651dc717c3b983b9d MD5 · raw file

  1. """Enumeration metaclass.
  2. XXX This is very much a work in progress.
  3. """
  4. import string
  5. class EnumMetaClass:
  6. """Metaclass for enumeration.
  7. To define your own enumeration, do something like
  8. class Color(Enum):
  9. red = 1
  10. green = 2
  11. blue = 3
  12. Now, Color.red, Color.green and Color.blue behave totally
  13. different: they are enumerated values, not integers.
  14. Enumerations cannot be instantiated; however they can be
  15. subclassed.
  16. """
  17. def __init__(self, name, bases, dict):
  18. """Constructor -- create an enumeration.
  19. Called at the end of the class statement. The arguments are
  20. the name of the new class, a tuple containing the base
  21. classes, and a dictionary containing everything that was
  22. entered in the class' namespace during execution of the class
  23. statement. In the above example, it would be {'red': 1,
  24. 'green': 2, 'blue': 3}.
  25. """
  26. for base in bases:
  27. if base.__class__ is not EnumMetaClass:
  28. raise TypeError, "Enumeration base class must be enumeration"
  29. bases = filter(lambda x: x is not Enum, bases)
  30. self.__name__ = name
  31. self.__bases__ = bases
  32. self.__dict = {}
  33. for key, value in dict.items():
  34. self.__dict[key] = EnumInstance(name, key, value)
  35. def __getattr__(self, name):
  36. """Return an enumeration value.
  37. For example, Color.red returns the value corresponding to red.
  38. XXX Perhaps the values should be created in the constructor?
  39. This looks in the class dictionary and if it is not found
  40. there asks the base classes.
  41. The special attribute __members__ returns the list of names
  42. defined in this class (it does not merge in the names defined
  43. in base classes).
  44. """
  45. if name == '__members__':
  46. return self.__dict.keys()
  47. try:
  48. return self.__dict[name]
  49. except KeyError:
  50. for base in self.__bases__:
  51. try:
  52. return getattr(base, name)
  53. except AttributeError:
  54. continue
  55. raise AttributeError, name
  56. def __repr__(self):
  57. s = self.__name__
  58. if self.__bases__:
  59. s = s + '(' + string.join(map(lambda x: x.__name__,
  60. self.__bases__), ", ") + ')'
  61. if self.__dict:
  62. list = []
  63. for key, value in self.__dict.items():
  64. list.append("%s: %s" % (key, int(value)))
  65. s = "%s: {%s}" % (s, string.join(list, ", "))
  66. return s
  67. class EnumInstance:
  68. """Class to represent an enumeration value.
  69. EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves
  70. like the integer 12 when compared, but doesn't support arithmetic.
  71. XXX Should it record the actual enumeration rather than just its
  72. name?
  73. """
  74. def __init__(self, classname, enumname, value):
  75. self.__classname = classname
  76. self.__enumname = enumname
  77. self.__value = value
  78. def __int__(self):
  79. return self.__value
  80. def __repr__(self):
  81. return "EnumInstance(%r, %r, %r)" % (self.__classname,
  82. self.__enumname,
  83. self.__value)
  84. def __str__(self):
  85. return "%s.%s" % (self.__classname, self.__enumname)
  86. def __cmp__(self, other):
  87. return cmp(self.__value, int(other))
  88. # Create the base class for enumerations.
  89. # It is an empty enumeration.
  90. Enum = EnumMetaClass("Enum", (), {})
  91. def _test():
  92. class Color(Enum):
  93. red = 1
  94. green = 2
  95. blue = 3
  96. print Color.red
  97. print dir(Color)
  98. print Color.red == Color.red
  99. print Color.red == Color.blue
  100. print Color.red == 1
  101. print Color.red == 2
  102. class ExtendedColor(Color):
  103. white = 0
  104. orange = 4
  105. yellow = 5
  106. purple = 6
  107. black = 7
  108. print ExtendedColor.orange
  109. print ExtendedColor.red
  110. print Color.red == ExtendedColor.red
  111. class OtherColor(Enum):
  112. white = 4
  113. blue = 5
  114. class MergedColor(Color, OtherColor):
  115. pass
  116. print MergedColor.red
  117. print MergedColor.white
  118. print Color
  119. print ExtendedColor
  120. print OtherColor
  121. print MergedColor
  122. if __name__ == '__main__':
  123. _test()