diff options
Diffstat (limited to 'pypers/meta/metatype.html')
-rwxr-xr-x | pypers/meta/metatype.html | 197 |
1 files changed, 0 insertions, 197 deletions
diff --git a/pypers/meta/metatype.html b/pypers/meta/metatype.html deleted file mode 100755 index f83b6bb..0000000 --- a/pypers/meta/metatype.html +++ /dev/null @@ -1,197 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<meta name="generator" content="Docutils 0.2.8: http://docutils.sourceforge.net/" /> -<title>SOLVING THE METACLASS CONFLICT</title> -<link rel="stylesheet" href="default.css" type="text/css" /> -</head> -<body> -<div class="document" id="solving-the-metaclass-conflict"> -<h1 class="title">SOLVING THE METACLASS CONFLICT</h1> -<div class="section" id="summary"> -<h1><a name="summary">Summary:</a></h1> -<p>Any serious user of metaclasses has been bitten at least once by the -infamous metaclass/metatype conflict. Here I give a general recipe to -solve the problem, as well as some theory and some examples.</p> -<blockquote> -<pre class="literal-block"> -#<noconflict.py> - -def _generatemetaclass(bases,metas): - """Internal function called by inferred. 'bases' is tuple of base classes - and 'metas' a tuple of metaclasses.""" - - metas=[m for m in metas if m is not type] - trivial=lambda m: m is type or m in metas - metabases=metas+[mb for mb in map(type,bases) if not trivial(mb)] - metaname="_"+''.join([m.__name__ for m in metabases]) - if not metabases: # trivial metabase - return type - elif len(metabases)==1: # single metabase - return metabases[0] - else: # multiple metabases - # creates new metaclass,shift possible conflict to meta-metaclasses - return type(metaname,tuple(metabases),{}) - -def memoize(f): - """This closure remembers all f invocations""" - argskw,result = [],[] - def _(*args,**kw): - akw=args,kw - try: # returns a previously stored result - return result[argskw.index(akw)] - except ValueError: # there is no previously stored result - argskw.append(akw) # update argskw - result.append(f(*args,**kw)) # update result - return result[-1] # return the new result - _.argskw=argskw #makes the argskw list accessible outside - _.result=result #makes the result list accessible outside - return _ - -_generatemetaclass=memoize(_generatemetaclass) - -def inferred(*metas): - """Class factory avoiding metatype conflicts: if the base classes have - metaclasses conflicting within themselves or with the given metaclass, - it automatically generates a compatible metaclass and instantiate the - inferred class from it.""" - return lambda n,b,d: _generatemetaclass(b,metas or (type,))(n,b,d) - -#</noconflict.py> -</pre> -</blockquote> -</div> -<div class="section" id="discussion"> -<h1><a name="discussion">Discussion</a></h1> -<p>I think that not too many programmers are familiar with metaclasses and -metatype conflicts, therefore let me be pedagogical ;)</p> -<p>The simplest case where a metatype conflict happens is the following. -Consider a class <tt class="literal"><span class="pre">A</span></tt> with metaclass <tt class="literal"><span class="pre">M_A</span></tt> and a class <tt class="literal"><span class="pre">B</span></tt> with -an independent metaclass <tt class="literal"><span class="pre">M_B</span></tt>; suppose we derive <tt class="literal"><span class="pre">C</span></tt> from <tt class="literal"><span class="pre">A</span></tt> -and <tt class="literal"><span class="pre">B</span></tt>. The question is: what is the metaclass of <tt class="literal"><span class="pre">C</span></tt> ? -Is it <tt class="literal"><span class="pre">M_A</span></tt> or <tt class="literal"><span class="pre">M_B</span></tt> ?</p> -<blockquote> -<pre class="doctest-block"> ->>> class M_A(type): -... pass ->>> class M_B(type): -... pass ->>> class A(object): -... __metaclass__=M_A ->>> class B(object): -... __metaclass__=M_B ->>> class C(A,B): -... pass -Traceback (most recent call last): - File "<stdin>", line 1, in ? -TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases -</pre> -</blockquote> -<p>The correct answer (see the book "Putting metaclasses to work" for a -thought discussion) is <tt class="literal"><span class="pre">M_C</span></tt>, where <tt class="literal"><span class="pre">M_C</span></tt> is a metaclass that inherits -from <tt class="literal"><span class="pre">M_A</span></tt> and <tt class="literal"><span class="pre">M_B</span></tt>, as in the following graph, where instantiation -is denoted by colon lines:</p> -<blockquote> -<pre class="literal-block"> -M_A M_B - : \ / : - : \ / : - A M_C B - \ : / - \ : / - C -</pre> -</blockquote> -<p>However, Python is not that magic, and it does not automatically create -<tt class="literal"><span class="pre">M_C</span></tt>. Instead, it raises a <tt class="literal"><span class="pre">TypeError</span></tt>, warning the programmer of -the possible confusion. The metatype conflict can be avoided -by assegning the correct metaclass to <tt class="literal"><span class="pre">C</span></tt> by hand:</p> -<blockquote> -<pre class="doctest-block"> ->>> class M_AM_B(M_A,M_B): pass -... ->>> class C(A,B): -... __metaclass__=M_AM_B ->>> C,type(C) -(<class 'C'>, <class 'M_AM_B'>) -</pre> -</blockquote> -<p>In general, a class <tt class="literal"><span class="pre">A(B,</span> <span class="pre">C,</span> <span class="pre">D</span> <span class="pre">,</span> <span class="pre">...)</span></tt> can be generated without conflicts -only if <tt class="literal"><span class="pre">type(A)</span></tt> is a subclass of each of <tt class="literal"><span class="pre">type(B),</span> <span class="pre">type(C),</span> <span class="pre">...</span></tt></p> -<p>It is possible to automatically avoid conflicts, by defining a smart -class factory that generates the correct metaclass by looking at the -metaclasses of the base classes. This is done via the <tt class="literal"><span class="pre">inferred</span></tt> -class factory, wich internally invokes the <tt class="literal"><span class="pre">_generatemetaclass</span></tt> -function. When <tt class="literal"><span class="pre">_generatemetaclass</span></tt> is invoked with the same bases and -the same metaclasses it should return the same metaclass. This is done by -keeping a list of the generated metaclasses thanks to the <tt class="literal"><span class="pre">with_memory</span></tt> -closure. Now, when <tt class="literal"><span class="pre">_generatemetaclass</span></tt> is invoked with the same arguments -it returns the same metaclass.</p> -<blockquote> -<pre class="doctest-block"> ->>> import sys; sys.path.append('.') # for doctest purposes ->>> from noconflict import inferred ->>> class C(A,B): -... __metaclass__=inferred() ->>> C -<class 'C'> ->>> type(C) # automatically generated metaclass -<class 'noconflict._M_AM_B'> -</pre> -<pre class="doctest-block"> ->>> class M_A(type): pass -... ->>> class M_B(type): pass -... ->>> class A: __metaclass__=M_A -... ->>> class B: __metaclass__=M_B -... ->>> class E(A,B): -... __metaclass__=inferred() ->>> type(E) -<class 'noconflict._M_AM_B'> ->>> class F(A,B): -... __metaclass__=inferred() ->>> type(F) -<class 'noconflict._M_AM_B'> ->>> type(E) is type(F) -True -</pre> -</blockquote> -<p>Another example where <tt class="literal"><span class="pre">inferred()</span></tt> can solve the conflict is the -following:</p> -<blockquote> -<pre class="doctest-block"> ->>> class D(A): -... __metaclass__=M_B -Traceback (most recent call last): - File "<string>", line 1, in ? -TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases -</pre> -</blockquote> -<p>Here the problem is that since <tt class="literal"><span class="pre">D</span></tt> inherits from <tt class="literal"><span class="pre">A</span></tt>, its metaclass -must inherit from <tt class="literal"><span class="pre">M_A</span></tt> and cannot be <tt class="literal"><span class="pre">M_B</span></tt>.</p> -<p><tt class="literal"><span class="pre">inferred</span></tt> solves the problem by automatically inheriting both from -<tt class="literal"><span class="pre">M_B</span></tt> and <tt class="literal"><span class="pre">M_A</span></tt>:</p> -<blockquote> -<pre class="doctest-block"> ->>> class D(A): -... __metaclass__=inferred(M_B) ->>> type(D) -<class 'noconflict._M_BM_A'> -</pre> -</blockquote> -<p>Note: these examples here have been checked with doctest on Python 2.3b1.</p> -</div> -</div> -<hr class="footer"/> -<div class="footer"> -<a class="reference" href="metatype.txt">View document source</a>. -Generated on: 2003-06-06 22:08 UTC. -Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. -</div> -</body> -</html> |