Examples of metaprogramming in pure Python.

This commit is contained in:
Guido van Rossum 1997-08-23 21:14:37 +00:00
parent 558f66ff53
commit bff110f3f1
2 changed files with 291 additions and 0 deletions

165
Demo/metaclasses/Enum.py Normal file
View File

@ -0,0 +1,165 @@
"""Enumeration metaclass."""
import string
class EnumMetaClass:
"""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__(self, name, bases, dict):
"""Constructor -- create an enumeration.
Called at the end of the class statement. The arguments are
the name of the new class, a tuple containing the base
classes, and a dictionary containing everything that was
entered in the class' namespace during execution of the class
statement. In the above example, it would be {'red': 1,
'green': 2, 'blue': 3}.
"""
for base in bases:
if base.__class__ is not EnumMetaClass:
raise TypeError, "Enumeration base class must be enumeration"
bases = filter(lambda x: x is not Enum, bases)
self.__name__ = name
self.__bases__ = bases
self.__dict = {}
for key, value in dict.items():
self.__dict[key] = EnumInstance(name, key, value)
def __getattr__(self, name):
"""Return an enumeration value.
For example, Color.red returns the value corresponding to red.
XXX Perhaps the values should be created in the constructor?
This looks in the class dictionary and if it is not found
there asks the base classes.
The special attribute __members__ returns the list of names
defined in this class (it does not merge in the names defined
in base classes).
"""
if name == '__members__':
return self.__dict.keys()
try:
return self.__dict[name]
except KeyError:
for base in self.__bases__:
try:
return getattr(base, name)
except AttributeError:
continue
raise AttributeError, name
def __repr__(self):
s = self.__name__
if self.__bases__:
s = s + '(' + string.join(map(lambda x: x.__name__,
self.__bases__), ", ") + ')'
if self.__dict:
list = []
for key, value in self.__dict.items():
list.append("%s: %s" % (key, int(value)))
s = "%s: {%s}" % (s, string.join(list, ", "))
return s
class EnumInstance:
"""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 __init__(self, classname, enumname, value):
self.__classname = classname
self.__enumname = enumname
self.__value = value
def __int__(self):
return self.__value
def __repr__(self):
return "EnumInstance(%s, %s, %s)" % (`self.__classname`,
`self.__enumname`,
`self.__value`)
def __str__(self):
return "%s.%s" % (self.__classname, self.__enumname)
def __cmp__(self, other):
return cmp(self.__value, int(other))
# Create the base class for enumerations.
# It is an empty enumeration.
Enum = EnumMetaClass("Enum", (), {})
def _test():
class Color(Enum):
red = 1
green = 2
blue = 3
print Color.red
print dir(Color)
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
if __name__ == '__main__':
_test()

126
Demo/metaclasses/Trace.py Normal file
View File

@ -0,0 +1,126 @@
"""Tracing metaclass."""
import types
class TraceMetaClass:
"""Metaclass for tracing.
Classes defined using this metaclass have an automatic tracing
feature -- by setting the __trace_output__ instance (or class)
variable to a file object, trace messages about all calls are
written to the file. The trace formatting can be changed by
defining a suitable __trace_call__ method.
"""
__inited = 0
def __init__(self, name, bases, dict):
self.__name__ = name
self.__bases__ = bases
self.__dict = dict
# XXX Can't define __dict__, alas
self.__inited = 1
def __getattr__(self, name):
try:
return self.__dict[name]
except KeyError:
for base in self.__bases__:
try:
return getattr(base, name)
except AttributeError:
pass
raise AttributeError, name
def __setattr__(self, name, value):
if not self.__inited:
self.__dict__[name] = value
else:
self.__dict[name] = value
def __call__(self, *args, **kw):
inst = TracingInstance()
inst.__meta_init__(self)
try:
init = inst.__getattr__('__init__')
except AttributeError:
init = lambda: None
apply(init, args, kw)
return inst
__trace_output__ = None
class TracingInstance:
"""Helper class to represent an instance of a tracing class."""
def __trace_call__(self, fp, fmt, *args):
fp.write((fmt+'\n') % args)
def __meta_init__(self, klass):
self.__class = klass
def __getattr__(self, name):
# Invoked for any attr not in the instance's __dict__
try:
raw = self.__class.__getattr__(name)
except AttributeError:
raise AttributeError, name
if type(raw) != types.FunctionType:
return raw
# It's a function
fullname = self.__class.__name__ + "." + name
if not self.__trace_output__ or name == '__trace_call__':
return NotTracingWrapper(fullname, raw, self)
else:
return TracingWrapper(fullname, raw, self)
class NotTracingWrapper:
def __init__(self, name, func, inst):
self.__name__ = name
self.func = func
self.inst = inst
def __call__(self, *args, **kw):
return apply(self.func, (self.inst,) + args, kw)
class TracingWrapper(NotTracingWrapper):
def __call__(self, *args, **kw):
self.inst.__trace_call__(self.inst.__trace_output__,
"calling %s, inst=%s, args=%s, kw=%s",
self.__name__, self.inst, args, kw)
try:
rv = apply(self.func, (self.inst,) + args, kw)
except:
t, v, tb = sys.exc_info()
self.inst.__trace_call__(self.inst.__trace_output__,
"returning from %s with exception %s: %s",
self.__name__, t, v)
raise t, v, tb
else:
self.inst.__trace_call__(self.inst.__trace_output__,
"returning from %s with value %s",
self.__name__, rv)
return rv
Traced = TraceMetaClass('Traced', (), {'__trace_output__': None})
def _test():
import sys
class C(Traced):
def __init__(self, x=0): self.x = x
def m1(self, x): self.x = x
def m2(self, y): return self.x + y
C.__trace_output__ = sys.stdout
x = C(4321)
print x
print x.x
print x.m1(100)
print x.m1(10)
print x.m2(33)
print x.m1(5)
print x.m2(4000)
print x.x
if __name__ == '__main__':
_test()