Order in metaclass
Bengt Richter
bokr at oz.net
Wed Oct 13 05:39:36 EDT 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