Order in metaclass

Bengt Richter bokr at oz.net
Wed Oct 13 11:39:36 CEST 2004


On Wed, 13 Oct 2004 09:04:03 +0200, Peter Otten <__peter__ at web.de> wrote:

>Nicolas Fleury wrote:
>
>> In the following example:
>> 
>> class MyMetaclass(type): pass
>> class MyBaseType(object): __metaclass__ = MyMetaclass
>> class MyType(MyBaseType):
>>      x = 4
>>      y = 5
>>      z = 6
>> 
>> Is there any way to modify MyMetaclass to keep the order of x,y,z
>> somewhere?
>
>If you want to record the order of these definitions, you need to pass a
>custom dictionary that keeps track of assignments in the class generation
>code (basically a normal python function comprising the class suite).
>Unfortunately that dictionary - which you see later as the classdict
>parameter of the metaclass __new__() method - is always a dict created in C,
>as was recently discussed on c.l.py (sorry, but all keywords I remember are
>'metaclass' and 'martelli' - not very selective :-). Below is my (clumsy)
>attempt for a workaround:
>
>import itertools
>
>class OrderedValue(object):
>    newIndex = itertools.count(1).next
>    def __init__(self, value):
>        self.value = value
>        self.index = self.newIndex()
>        
>class Meta(type):
>    def __new__(mcl, name, bases, classdict):
>        assert "ordered_names" not in classdict
>        
>        values = []
>        for (n, v) in classdict.iteritems():
>            try:
>                v, i = v.value, v.index
>            except AttributeError:
>                pass
>            else:
>                values.append((i, n, v))
>        values.sort()
>
>        ordered_names = []
>        for (i, n, v) in values:
>            ordered_names.append(n)
>            classdict[n] = v
>        classdict["ordered_names"] = ordered_names
>        
>        return type.__new__(mcl, name, bases, classdict)
>        
>                
>class Base:
>    __metaclass__ = Meta
>    
>class Demo(Base):
>    alpha = 0
>    beta = OrderedValue(1)
>    gamma = OrderedValue(17)
>    delta = OrderedValue(3)
>    
>print Demo.ordered_names        
>print Demo.alpha, Demo.beta
>

Or, an ugly hack that might work for a while, depending on how
co_names is really generated. It seems in order of occurrence
(including right hand sides of assignment, but still top down) ...

 >>> import sys
 >>> def getnames(): return sys._getframe(1).f_code.co_names
 ...
 >>> def MC(cname, cbases, cdict):
 ...     names = cdict.get('ordic',[])
 ...     names = [name for name in names if name in cdict]
 ...     cdict['ordic'] = dict([(name,i) for i,name in enumerate(names)])
 ...     return type(cname, cbases, cdict)
 ...
 >>> class C(object):
 ...     __metaclass__ = MC # really a function shortcut
 ...     x = 123
 ...     y = sys
 ...     z = 0
 ...     ordic = getnames()
 ...
 >>> C.ordic
 {'ordic': 5, '__module__': 0, '__metaclass__': 1, 'y': 3, 'x': 2, 'z': 4}

 >>> class D(object):
 ...     __metaclass__ = MC # really a function shortcut
 ...     x = 123
 ...     def m(self): pass
 ...     def sm(): print getnames()
 ...     sm = staticmethod(sm)
 ...     ordic = getnames()
 ...
 >>> D.ordic
 {'ordic': 5, '__module__': 0, '__metaclass__': 1, 'm': 3, 'sm': 4, 'x': 2}
 >>> D.sm
 <function sm at 0x00906030>
 >>> D.sm()
 ('getnames',)

Regards,
Bengt Richter



More information about the Python-list mailing list