Getting the definition order of class attributes in python

Same question was in StackOverflow, but the answer was not so soft on me.

It gave me a some hints, but it actually says ‘Watch a code’. Ok, so I read the code and understood that behavior, I will write that description here.

Why is definition order necessary

The best example is on Form libraries. The definition order will affect to rendering order directory.

Consider on a suprious form library, like this:

class MyForm(Form):
    name = StringField()
    text = StringField()

and it should be rendered that the first place is name and the second text like this oreder:

<input value='name' />
<input value='text' />

Of cause, general form libraries consider the definition order, such as Djnago Form, deform (actually, colander’s behavior).

On my case, I should handle it on creating Uiro framework, definition views in controller.

Answer

To handle the order, you should place a counter. The counter will be increased on each constraction of orderd attributes. And then, each attributes stores that counter value to it’s own.

>>> import itertools
>>> class Field(object):
...     _counter = itertools.count()
...     def __init__(self):
...         self._order = next(Field._counter)
...
>>> class Form(object):
...     name = Field()
...     text = Field()
...
>>> Form.name
<__main__.Field object at 0x7f0abfb26250>
>>> Form.name._order
0
>>> Form.text._order
1

Ok, seems good. Then, apply this practice on writing metaclass:

>>> class FormMetaClass(type):
...     def __new__(cls, name, base, attrs):
...         new_class = super(FormMetaClass, cls).__new__(cls, name, base, attrs)
...
...         fields = [(name, value) for name, value in attrs.items()
...                   if isinstance(value, Field)]
...
...         # sorting manually corresponds to the definision order of Fields.
...         fields.sort(key=lambda e: e[1]._order)
...
...         new_class.fields = fields
...         return new_class
...
>>> class Form(metaclass=FormMetaClass):
...     name = Field()
...     text = Field()
...
>>> Form.fields[0]
('name', <__main__.Field object at 0x7f0abfb26410>)
>>> Form.fields[1]
('text', <__main__.Field object at 0x7f0abfb26490>)

sweet, the definition order didn’t lost. In __new__ method, the fields before sorted, the order of Fields is not considered in fields, because the attrs is just a dictionary. so we should apply _oreder attribute to Field and re-consider the order manually using that _order.

I used this tips on this change, check it out.