mirror of
https://github.com/python/cpython.git
synced 2025-11-02 03:01:58 +00:00
Add Enum and Eiffel examples using new-style classes.
This commit is contained in:
parent
20e5abc86e
commit
a00331636b
2 changed files with 323 additions and 0 deletions
145
Demo/newmetaclasses/Eiffel.py
Normal file
145
Demo/newmetaclasses/Eiffel.py
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
"""Support Eiffel-style preconditions and postconditions."""
|
||||||
|
|
||||||
|
from new import function
|
||||||
|
|
||||||
|
class EiffelBaseMetaClass(type):
|
||||||
|
|
||||||
|
def convert_methods(cls, dict):
|
||||||
|
"""Replace functions in dict with EiffelMethod wrappers.
|
||||||
|
|
||||||
|
The dict is modified in place.
|
||||||
|
|
||||||
|
If a method ends in _pre or _post, it is removed from the dict
|
||||||
|
regardless of whether there is a corresponding method.
|
||||||
|
"""
|
||||||
|
# find methods with pre or post conditions
|
||||||
|
methods = []
|
||||||
|
prenpost = []
|
||||||
|
for k, v in dict.iteritems():
|
||||||
|
if k.endswith('_pre') or k.endswith('_post'):
|
||||||
|
assert isinstance(v, function)
|
||||||
|
prenpost.append(k)
|
||||||
|
elif isinstance(v, function):
|
||||||
|
methods.append(k)
|
||||||
|
for m in methods:
|
||||||
|
pre = dict.get("%s_pre" % m)
|
||||||
|
post = dict.get("%s_post" % m)
|
||||||
|
if pre or post:
|
||||||
|
dict[k] = cls.make_eiffel_method(dict[m], pre, post)
|
||||||
|
|
||||||
|
convert_methods = classmethod(convert_methods)
|
||||||
|
|
||||||
|
def make_eiffel_method(func, pre, post):
|
||||||
|
def method(self, *args, **kwargs):
|
||||||
|
if pre:
|
||||||
|
pre(self, *args, **kwargs)
|
||||||
|
x = func(self, *args, **kwargs)
|
||||||
|
if post:
|
||||||
|
post(self, x, *args, **kwargs)
|
||||||
|
return x
|
||||||
|
|
||||||
|
if func.__doc__:
|
||||||
|
method.__doc__ = func.__doc__
|
||||||
|
|
||||||
|
return method
|
||||||
|
|
||||||
|
make_eiffel_method = staticmethod(make_eiffel_method)
|
||||||
|
|
||||||
|
class EiffelMetaClass1(EiffelBaseMetaClass):
|
||||||
|
# an implementation of the "eiffel" meta class that uses nested functions
|
||||||
|
|
||||||
|
def __new__(meta, name, bases, dict):
|
||||||
|
meta.convert_methods(dict)
|
||||||
|
return super(EiffelMetaClass1, meta).__new__(meta, name, bases, dict)
|
||||||
|
|
||||||
|
class EiffelMethodWrapper:
|
||||||
|
|
||||||
|
def __init__(self, inst, descr):
|
||||||
|
self._inst = inst
|
||||||
|
self._descr = descr
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
return self._descr.callmethod(self._inst, args, kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EiffelDescriptor(object):
|
||||||
|
|
||||||
|
def __init__(self, func, pre, post):
|
||||||
|
self._func = func
|
||||||
|
self._pre = pre
|
||||||
|
self._post = post
|
||||||
|
|
||||||
|
self.__name__ = func.__name__
|
||||||
|
self.__doc__ = func.__doc__
|
||||||
|
|
||||||
|
def __get__(self, obj, cls):
|
||||||
|
return EiffelMethodWrapper(obj, self)
|
||||||
|
|
||||||
|
def callmethod(self, inst, args, kwargs):
|
||||||
|
if self._pre:
|
||||||
|
self._pre(inst, *args, **kwargs)
|
||||||
|
x = self._func(inst, *args, **kwargs)
|
||||||
|
if self._post:
|
||||||
|
self._post(inst, x, *args, **kwargs)
|
||||||
|
return x
|
||||||
|
|
||||||
|
class EiffelMetaClass2(EiffelMetaClass1):
|
||||||
|
# an implementation of the "eiffel" meta class that uses descriptors
|
||||||
|
|
||||||
|
make_eiffel_method = EiffelDescriptor
|
||||||
|
|
||||||
|
def _test(metaclass):
|
||||||
|
class Eiffel:
|
||||||
|
__metaclass__ = metaclass
|
||||||
|
|
||||||
|
class Test(Eiffel):
|
||||||
|
|
||||||
|
def m(self, arg):
|
||||||
|
"""Make it a little larger"""
|
||||||
|
return arg + 1
|
||||||
|
|
||||||
|
def m2(self, arg):
|
||||||
|
"""Make it a little larger"""
|
||||||
|
return arg + 1
|
||||||
|
|
||||||
|
def m2_pre(self, arg):
|
||||||
|
assert arg > 0
|
||||||
|
|
||||||
|
def m2_post(self, result, arg):
|
||||||
|
assert result > arg
|
||||||
|
|
||||||
|
class Sub(Test):
|
||||||
|
def m2(self, arg):
|
||||||
|
return arg**2
|
||||||
|
def m2_post(self, Result, arg):
|
||||||
|
super(Sub, self).m2_post(Result, arg)
|
||||||
|
assert Result < 100
|
||||||
|
|
||||||
|
t = Test()
|
||||||
|
t.m(1)
|
||||||
|
t.m2(1)
|
||||||
|
try:
|
||||||
|
t.m2(0)
|
||||||
|
except AssertionError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
s = Sub()
|
||||||
|
try:
|
||||||
|
s.m2(1)
|
||||||
|
except AssertionError:
|
||||||
|
pass # result == arg
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
try:
|
||||||
|
s.m2(10)
|
||||||
|
except AssertionError:
|
||||||
|
pass # result == 100
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
_test(EiffelMetaClass1)
|
||||||
|
_test(EiffelMetaClass2)
|
||||||
|
|
||||||
178
Demo/newmetaclasses/Enum.py
Normal file
178
Demo/newmetaclasses/Enum.py
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
"""Enumeration metaclass."""
|
||||||
|
|
||||||
|
class EnumMetaclass(type):
|
||||||
|
"""Metaclass for enumeration.
|
||||||
|
|
||||||
|
To define your own enumeration, do something like
|
||||||
|
|
||||||
|
class Color(Enum):
|
||||||
|
red = 1
|
||||||
|
green = 2
|
||||||
|
blue = 3
|
||||||
|
|
||||||
|
Now, Color.red, Color.green and Color.blue behave totally
|
||||||
|
different: they are enumerated values, not integers.
|
||||||
|
|
||||||
|
Enumerations cannot be instantiated; however they can be
|
||||||
|
subclassed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(cls, name, bases, dict):
|
||||||
|
super(EnumMetaclass, cls).__init__(name, bases, dict)
|
||||||
|
cls._members = []
|
||||||
|
for attr in dict.keys():
|
||||||
|
if not (attr.startswith('__') and attr.endswith('__')):
|
||||||
|
enumval = EnumInstance(name, attr, dict[attr])
|
||||||
|
setattr(cls, attr, enumval)
|
||||||
|
cls._members.append(attr)
|
||||||
|
|
||||||
|
def __getattr__(cls, name):
|
||||||
|
if name == "__members__":
|
||||||
|
return cls._members
|
||||||
|
raise AttributeError, name
|
||||||
|
|
||||||
|
def __repr__(cls):
|
||||||
|
s1 = s2 = ""
|
||||||
|
enumbases = [base.__name__ for base in cls.__bases__
|
||||||
|
if isinstance(base, EnumMetaclass) and not base is Enum]
|
||||||
|
if enumbases:
|
||||||
|
s1 = "(%s)" % ", ".join(enumbases)
|
||||||
|
enumvalues = ["%s: %d" % (val, getattr(cls, val))
|
||||||
|
for val in cls._members]
|
||||||
|
if enumvalues:
|
||||||
|
s2 = ": {%s}" % ", ".join(enumvalues)
|
||||||
|
return "%s%s%s" % (cls.__name__, s1, s2)
|
||||||
|
|
||||||
|
class FullEnumMetaclass(EnumMetaclass):
|
||||||
|
"""Metaclass for full enumerations.
|
||||||
|
|
||||||
|
A full enumeration displays all the values defined in base classes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(cls, name, bases, dict):
|
||||||
|
super(FullEnumMetaclass, cls).__init__(name, bases, dict)
|
||||||
|
for obj in cls.__mro__:
|
||||||
|
if isinstance(obj, EnumMetaclass):
|
||||||
|
for attr in obj._members:
|
||||||
|
# XXX inefficient
|
||||||
|
if not attr in cls._members:
|
||||||
|
cls._members.append(attr)
|
||||||
|
|
||||||
|
class EnumInstance(int):
|
||||||
|
"""Class to represent an enumeration value.
|
||||||
|
|
||||||
|
EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves
|
||||||
|
like the integer 12 when compared, but doesn't support arithmetic.
|
||||||
|
|
||||||
|
XXX Should it record the actual enumeration rather than just its
|
||||||
|
name?
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __new__(cls, classname, enumname, value):
|
||||||
|
return int.__new__(cls, value)
|
||||||
|
|
||||||
|
def __init__(self, classname, enumname, value):
|
||||||
|
self.__classname = classname
|
||||||
|
self.__enumname = enumname
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "EnumInstance(%s, %s, %d)" % (self.__classname, self.__enumname,
|
||||||
|
self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s.%s" % (self.__classname, self.__enumname)
|
||||||
|
|
||||||
|
class Enum:
|
||||||
|
__metaclass__ = EnumMetaclass
|
||||||
|
|
||||||
|
class FullEnum:
|
||||||
|
__metaclass__ = FullEnumMetaclass
|
||||||
|
|
||||||
|
def _test():
|
||||||
|
|
||||||
|
class Color(Enum):
|
||||||
|
red = 1
|
||||||
|
green = 2
|
||||||
|
blue = 3
|
||||||
|
|
||||||
|
print Color.red
|
||||||
|
|
||||||
|
print `Color.red`
|
||||||
|
print Color.red == Color.red
|
||||||
|
print Color.red == Color.blue
|
||||||
|
print Color.red == 1
|
||||||
|
print Color.red == 2
|
||||||
|
|
||||||
|
class ExtendedColor(Color):
|
||||||
|
white = 0
|
||||||
|
orange = 4
|
||||||
|
yellow = 5
|
||||||
|
purple = 6
|
||||||
|
black = 7
|
||||||
|
|
||||||
|
print ExtendedColor.orange
|
||||||
|
print ExtendedColor.red
|
||||||
|
|
||||||
|
print Color.red == ExtendedColor.red
|
||||||
|
|
||||||
|
class OtherColor(Enum):
|
||||||
|
white = 4
|
||||||
|
blue = 5
|
||||||
|
|
||||||
|
class MergedColor(Color, OtherColor):
|
||||||
|
pass
|
||||||
|
|
||||||
|
print MergedColor.red
|
||||||
|
print MergedColor.white
|
||||||
|
|
||||||
|
print Color
|
||||||
|
print ExtendedColor
|
||||||
|
print OtherColor
|
||||||
|
print MergedColor
|
||||||
|
|
||||||
|
def _test2():
|
||||||
|
|
||||||
|
class Color(FullEnum):
|
||||||
|
red = 1
|
||||||
|
green = 2
|
||||||
|
blue = 3
|
||||||
|
|
||||||
|
print Color.red
|
||||||
|
|
||||||
|
print `Color.red`
|
||||||
|
print Color.red == Color.red
|
||||||
|
print Color.red == Color.blue
|
||||||
|
print Color.red == 1
|
||||||
|
print Color.red == 2
|
||||||
|
|
||||||
|
class ExtendedColor(Color):
|
||||||
|
white = 0
|
||||||
|
orange = 4
|
||||||
|
yellow = 5
|
||||||
|
purple = 6
|
||||||
|
black = 7
|
||||||
|
|
||||||
|
print ExtendedColor.orange
|
||||||
|
print ExtendedColor.red
|
||||||
|
|
||||||
|
print Color.red == ExtendedColor.red
|
||||||
|
|
||||||
|
class OtherColor(FullEnum):
|
||||||
|
white = 4
|
||||||
|
blue = 5
|
||||||
|
|
||||||
|
class MergedColor(Color, OtherColor):
|
||||||
|
pass
|
||||||
|
|
||||||
|
print MergedColor.red
|
||||||
|
print MergedColor.white
|
||||||
|
|
||||||
|
print Color
|
||||||
|
print ExtendedColor
|
||||||
|
print OtherColor
|
||||||
|
print MergedColor
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
_test()
|
||||||
|
_test2()
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue