Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

Getting the definition order of class attributes in python

Getting the definition order of class attributes in python

Same question was in StackOverFllow, 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.