Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1fb071c

Browse files
committed
Checkpoint.
1 parent b2173c3 commit 1fb071c

2 files changed

Lines changed: 369 additions & 5 deletions

File tree

Demo/metaclasses/Meta.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ def __getattr__(self, name):
2929
raw = self.__formalclass__.__getattr__(name)
3030
except AttributeError:
3131
try:
32-
_getattr_ = self.__dict__['_getattr_']
32+
ga = self.__formalclass__.__getattr__('__usergetattr__')
3333
except KeyError:
3434
raise AttributeError, name
35-
return _getattr_(name)
35+
return ga(self, name)
3636
if type(raw) != types.FunctionType:
3737
return raw
3838
return self.__methodwrapper__(raw, self)
@@ -50,8 +50,13 @@ class MetaClass:
5050
__inited = 0
5151

5252
def __init__(self, name, bases, dict):
53-
if dict.has_key('__getattr__'):
54-
raise TypeError, "Can't override __getattr__; use _getattr_"
53+
try:
54+
ga = dict['__getattr__']
55+
except KeyError:
56+
pass
57+
else:
58+
dict['__usergetattr__'] = ga
59+
del dict['__getattr__']
5560
self.__name__ = name
5661
self.__bases__ = bases
5762
self.__realdict__ = dict
@@ -98,7 +103,16 @@ def m1(self, x):
98103
x = C()
99104
print x
100105
x.m1(12)
101-
106+
class D(C):
107+
def __getattr__(self, name):
108+
if name[:2] == '__': raise AttributeError, name
109+
return "getattr:%s" % name
110+
x = D()
111+
print x.foo
112+
print x._foo
113+
## print x.__foo
114+
## print x.__foo__
115+
102116

103117
if __name__ == '__main__':
104118
_test()

Demo/metaclasses/index.html

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
<HTML>
2+
3+
<HEAD>
4+
<TITLE>Metaprogramming in Python 1.5</TITLE>
5+
</HEAD>
6+
7+
<BODY BGCOLOR="FFFFFF">
8+
9+
<H1>Metaprogramming in Python 1.5</H1>
10+
11+
<H4>XXX Don't link to this page! It is very much a work in progress.</H4>
12+
13+
<P>While Python 1.5 is only out as a <A
14+
HREF="http://grail.cnri.reston.va.us/python/1.5a3/">restricted alpha
15+
release</A>, its metaprogramming feature is worth mentioning.
16+
17+
<P>In previous Python releases (and still in 1.5), there is something
18+
called the ``Don Beaudry hook'', after its inventor and champion.
19+
This allows C extensions to provide alternate class behavior, thereby
20+
allowing the Python class syntax to be used to define other class-like
21+
entities. Don Beaudry has used this in his infamous <A
22+
HREF="http://maigret.cog.brown.edu/pyutil/">MESS</A> package; Jim
23+
Fulton has used it in his <A
24+
HREF="http://www.digicool.com/papers/ExtensionClass.html">Extension
25+
Classes</A> package. (It has also been referred to as the ``Don
26+
Beaudry <i>hack</i>, but that's a misnomer. There's nothing hackish
27+
about it -- in fact, it is rather elegant and deep, even though
28+
there's something dark to it.)
29+
30+
<P>Documentation of the Don Beaudry hook has purposefully been kept
31+
minimal, since it is a feature of incredible power, and is easily
32+
abused. Basically, it checks whether the <b>type of the base
33+
class</b> is callable, and if so, it is called to create the new
34+
class.
35+
36+
<P>Note the two indirection levels. Take a simple example:
37+
38+
<PRE>
39+
class B:
40+
pass
41+
42+
class C(B):
43+
pass
44+
</PRE>
45+
46+
Take a look at the second class definition, and try to fathom ``the
47+
type of the base class is callable.''
48+
49+
<P>(Types are not classes, by the way. See questions 4.2, 4.19 and in
50+
particular 6.22 in the <A
51+
HREF="http://grail.cnri.reston.va.us/cgi-bin/faqw.py" >Python FAQ</A>
52+
for more on this topic.)
53+
54+
<P>
55+
56+
<UL>
57+
58+
<LI>The <b>base class</b> is B; this one's easy.<P>
59+
60+
<LI>Since B is a class, its type is ``class''; so the <b>type of the
61+
base class</b> is the type ``class''. This is also known as
62+
types.ClassType, assuming the standard module <code>types</code> has
63+
been imported.<P>
64+
65+
<LI>Now is the type ``class'' <b>callable</b>? No, because types (in
66+
core Python) are never callable. Classes are callable (calling a
67+
class creates a new instance) but types aren't.<P>
68+
69+
</UL>
70+
71+
<P>So our conclusion is that in our example, the type of the base
72+
class (of C) is not callable. So the Don Beaudry hook does not apply,
73+
and the default class creation mechanism is used (which is also used
74+
when there is no base class). In fact, the Don Beaudry hook never
75+
applies when using only core Python, since the type of a core object
76+
is never callable.
77+
78+
<P>So what do Don and Jim do in order to use Don's hook? Write an
79+
extension that defines at least two new Python object types. The
80+
first would be the type for ``class-like'' objects usable as a base
81+
class, to trigger Don's hook. This type must be made callable.
82+
That's why we need a second type. Whether an object is callable
83+
depends on its type. So whether a type object is callable depends on
84+
<i>its</i> type, which is a <i>meta-type</i>. (In core Python there
85+
is only one meta-type, the type ``type'' (types.TypeType), which is
86+
the type of all type objects, even itself.) A new meta-type must
87+
be defined that makes the type of the class-like objects callable.
88+
(Normally, a third type would also be needed, the new ``instance''
89+
type, but this is not an absolute requirement -- the new class type
90+
could return an object of some existing type when invoked to create an
91+
instance.)
92+
93+
<P>Still confused? Here's a simple device due to Don himself to
94+
explain metaclasses. Take a simple class definition; assume B is a
95+
special class that triggers Don's hook:
96+
97+
<PRE>
98+
class C(B):
99+
a = 1
100+
b = 2
101+
</PRE>
102+
103+
This can be though of as equivalent to:
104+
105+
<PRE>
106+
C = type(B)('C', (B,), {'a': 1, 'b': 2})
107+
</PRE>
108+
109+
If that's too dense for you, here's the same thing written out using
110+
temporary variables:
111+
112+
<PRE>
113+
creator = type(B) # The type of the base class
114+
name = 'C' # The name of the new class
115+
bases = (B,) # A tuple containing the base class(es)
116+
namespace = {'a': 1, 'b': 2} # The namespace of the class statement
117+
C = creator(name, bases, namespace)
118+
</PRE>
119+
120+
This is analogous to what happens without the Don Beaudry hook, except
121+
that in that case the creator function is set to the default class
122+
creator.
123+
124+
<P>In either case, the creator is called with three arguments. The
125+
first one, <i>name</i>, is the name of the new class (as given at the
126+
top of the class statement). The <i>bases</i> argument is a tuple of
127+
base classes (a singleton tuple if there's only one base class, like
128+
the example). Finally, <i>namespace</i> is a dictionary containing
129+
the local variables collected during execution of the class statement.
130+
131+
<P>Note that the contents of the namespace dictionary is simply
132+
whatever names were defined in the class statement. A little-known
133+
fact is that when Python executes a class statement, it enters a new
134+
local namespace, and all assignments and function definitions take
135+
place in this namespace. Thus, after executing the following class
136+
statement:
137+
138+
<PRE>
139+
class C:
140+
a = 1
141+
def f(s): pass
142+
</PRE>
143+
144+
the class namespace's contents would be {'a': 1, 'f': &lt;function f
145+
...&gt;}.
146+
147+
<P>But enough already about Python metaprogramming in C; read the
148+
documentation of <A
149+
HREF="http://maigret.cog.brown.edu/pyutil/">MESS</A> or <A
150+
HREF="http://www.digicool.com/papers/ExtensionClass.html" >Extension
151+
Classes</A> for more information.
152+
153+
<H2>Writing Metaclasses in Python</H2>
154+
155+
<P>In Python 1.5, the requirement to write a C extension in order to
156+
engage in metaprogramming has been dropped (though you can still do
157+
it, of course). In addition to the check ``is the type of the base
158+
class callable,'' there's a check ``does the base class have a
159+
__class__ attribute.'' If so, it is assumed that the __class__
160+
attribute refers to a class.
161+
162+
<P>Let's repeat our simple example from above:
163+
164+
<PRE>
165+
class C(B):
166+
a = 1
167+
b = 2
168+
</PRE>
169+
170+
Assuming B has a __class__ attribute, this translates into:
171+
172+
<PRE>
173+
C = B.__class__('C', (B,), {'a': 1, 'b': 2})
174+
</PRE>
175+
176+
This is exactly the same as before except that instead of type(B),
177+
B.__class__ is invoked. If you have read <A HREF=
178+
"http://grail.cnri.reston.va.us/cgi-bin/faqw.py?req=show&file=faq06.022.htp"
179+
>FAQ question 6.22</A> you will understand that while there is a big
180+
technical difference between type(B) and B.__class__, they play the
181+
same role at different abstraction levels. And perhaps at some point
182+
in the future they will really be the same thing (at which point you
183+
would be able to derive subclasses from built-in types).
184+
185+
<P>Going back to the example, the class B.__class__ is instantiated,
186+
passing its constructor the same three arguments that are passed to
187+
the default class constructor or to an extension's metaprogramming
188+
code: <i>name</i>, <i>bases</i>, and <i>namespace</i>.
189+
190+
<P>It is easy to be confused by what exactly happens when using a
191+
metaclass, because we lose the absolute distinction between classes
192+
and instances: a class is an instance of a metaclass (a
193+
``metainstance''), but technically (i.e. in the eyes of the python
194+
runtime system), the metaclass is just a class, and the metainstance
195+
is just an instance. At the end of the class statement, the metaclass
196+
whose metainstance is used as a base class is instantiated, yielding a
197+
second metainstance (of the same metaclass). This metainstance is
198+
then used as a (normal, non-meta) class; instantiation of the class
199+
means calling the metainstance, and this will return a real instance.
200+
And what class is that an instance of? Conceptually, it is of course
201+
an instance of our metainstance; but in most cases the Python runtime
202+
system will see it as an instance of a a helper class used by the
203+
metaclass to implement its (non-meta) instances...
204+
205+
<P>Hopefully an example will make things clearer. Let's presume we
206+
have a metaclass MetaClass1. It's helper class (for non-meta
207+
instances) is callled HelperClass1. We now (manually) instantiate
208+
MetaClass1 once to get an empty special base class:
209+
210+
<PRE>
211+
BaseClass1 = MetaClass1("BaseClass1", (), {})
212+
</PRE>
213+
214+
We can now use BaseClass1 as a base class in a class statement:
215+
216+
<PRE>
217+
class MySpecialClass(BaseClass1):
218+
i = 1
219+
def f(s): pass
220+
</PRE>
221+
222+
At this point, MySpecialClass is defined; it is a metainstance of
223+
MetaClass1 just like BaseClass1, and in fact the expression
224+
``BaseClass1.__class__ == MySpecialClass.__class__ == MetaClass1''
225+
yields true.
226+
227+
<P>We are now ready to create instances of MySpecialClass. Let's
228+
assume that no constructor arguments are required:
229+
230+
<PRE>
231+
x = MySpecialClass()
232+
y = Myspecialclass()
233+
print x.__class__, y.__class__
234+
</PRE>
235+
236+
The print statement shows that x and y are instances of HelperClass1.
237+
How did this happen? MySpecialClass is an instance of MetaClass1
238+
(``meta'' is irrelevant here); when an instance is called, its
239+
__call__ method is invoked, and presumably the __call__ method defined
240+
by MetaClass1 returns an instance of HelperClass1.
241+
242+
<P>Now let's see how we could use metaprogramming -- what can we do
243+
with metaclasses that we can't easily do without them? Here's one
244+
idea: a metaclass could automatically insert trace calls for all
245+
method calls. Let's first develop a simplified example, without
246+
support for inheritance or other ``advanced'' Python features (we'll
247+
add those later).
248+
249+
<PRE>
250+
import types
251+
252+
class Tracing:
253+
def __init__(self, name, bases, namespace):
254+
"""Create a new class."""
255+
self.__name__ = name
256+
self.__bases__ = bases
257+
self.__namespace__ = namespace
258+
def __call__(self):
259+
"""Create a new instance."""
260+
return Instance(self)
261+
262+
class Instance:
263+
def __init__(self, klass):
264+
self.__klass__ = klass
265+
def __getattr__(self, name):
266+
try:
267+
value = self.__klass__.__namespace__[name]
268+
except KeyError:
269+
raise AttributeError, name
270+
if type(value) is not types.FuncType:
271+
return value
272+
return BoundMethod(value, self)
273+
274+
class BoundMethod:
275+
def __init__(self, function, instance):
276+
self.function = function
277+
self.instance = instance
278+
def __call__(self, *args):
279+
print "calling", self.function, "for", instance, "with", args
280+
return apply(self.function, (self.instance,) + args)
281+
<HR>
282+
283+
Confused already?
284+
285+
286+
<P>XXX More text is needed here. For now, have a look at some very
287+
preliminary examples that I coded up to teach myself how to use this
288+
feature:
289+
290+
291+
292+
<H2>Real-life Examples</H2>
293+
294+
<DL>
295+
296+
<DT><A HREF="Enum.py">Enum.py</A>
297+
298+
<DD>This (ab)uses the class syntax as an elegant way to define
299+
enumerated types. The resulting classes are never instantiated --
300+
rather, their class attributes are the enumerated values. For
301+
example:
302+
303+
<PRE>
304+
class Color(Enum):
305+
red = 1
306+
green = 2
307+
blue = 3
308+
print Color.red
309+
</PRE>
310+
311+
will print the string ``Color.red'', while ``Color.red==1'' is true,
312+
and ``Color.red + 1'' raise a TypeError exception.
313+
314+
<P>
315+
316+
<DT><A HREF="Trace.py">Trace.py</A>
317+
318+
<DD>The resulting classes work much like standard classes, but by
319+
setting a special class or instance attribute __trace_output__ to
320+
point to a file, all calls to the class's methods are traced. It was
321+
a bit of a struggle to get this right. This should probably redone
322+
using the generic metaclass below.
323+
324+
<P>
325+
326+
<DT><A HREF="Meta.py">Meta.py</A>
327+
328+
<DD>A generic metaclass. This is an attempt at finding out how much
329+
standard class behavior can be mimicked by a metaclass. The
330+
preliminary answer appears to be that everything's fine as long as the
331+
class (or its clients) don't look at the instance's __class__
332+
attribute, nor at the class's __dict__ attribute. The use of
333+
__getattr__ internally makes the classic implementation of __getattr__
334+
hooks tough; we provide a similar hook _getattr_ instead.
335+
(__setattr__ and __delattr__ are not affected.)
336+
(XXX Hm. Could detect presence of __getattr__ and rename it.)
337+
338+
<P>
339+
340+
<DT><A HREF="Eiffel.py">Eiffel.py</A>
341+
342+
<DD>Uses the above generic metaclass to implement Eiffel style
343+
pre-conditions and post-conditions.
344+
345+
<P>
346+
</DL>
347+
348+
</BODY>
349+
350+
</HTML>

0 commit comments

Comments
 (0)