How to get the order in which class attributes are defined in Python?

I want to define light classes that should represent data structures. As with many data structures, the order of the data is important. Therefore, if I go further and define this:

class User(DataStructure): username = StringValue() password = StringValue() age = IntegerValue() 

I mean, this is a data structure that first enters a string with a username, then a string with a password, and finally, the age of the user as an integer.

If you are familiar with Python, you will find out that the above User class is an object that inherits from type . It will, like most other objects in Python, have __dict__ . And here lies my problem. This __dict__ is a hash map, so the order of class attributes in __dict__ is in no way related to their definition order.

Is there a way to determine the actual determination order? I ask here before I go with one of the less sensible methods that I can think of ...

Oh, and in order to be clear, I want this to come from the definition above: ['username', 'password', 'age']

+6
python oop class data-structures hash
source share
5 answers

This is something that is not entirely supported in Python. Django used metaclasses to handle this. See This Question: How does Django Know the Order for Visualizing Form Fields?

(Summary: see django.forms.forms.DeclarativeFieldsMetaclass , django.forms.forms.get_declared_fields and how creation_counter used in django.forms.fields.Field .)

+2
source share

Python 2.7 and 3.x define an OrderedDict in the collections module. I believe that it uses a linked list to maintain the insertion order of its elements. It adds repeating methods to standard variable display methods.

You can define a metaclass that uses OrderedDict , rather than a standard unordered dict as the __dict__ namespace for your data structure classes. If you give the metaclass the special __prepare__() method, you can do it. I have not tried this, but according to the docs this is possible:

From Python 3.1 Language Ref Section 3.3.3 Data Model - Setting Up Class Creation:

 If the metaclass has a __prepare__() attribute (usually implemented as a class or static method), it is called before the class body is evaluated with the name of the class and a tuple of its bases for arguments. It should return an object that supports the mapping interface that will be used to store the namespace of the class. The default is a plain dictionary. This could be used, for example, to keep track of the order that class attributes are declared in by returning an ordered dictionary. 

Unfortunately, the equivalent section 3.4.3 in Python 2.7 Language Ref does not mention the possibility of replacing the dict namespace and not mention the __prepare__() method. Thus, this is only possible in Python version 3.

+5
source share

One way that would be more general than the Django approach, __slots__ and available in python 2, would be this metaclass:

 class OrderedTypeMeta(type): def __new__(mcls, clsname, bases, clsdict): attrs = clsdict.get('_attrs_', []) attrnames = [] for name, value in attrs: clsdict[name] = value attrnames.append(name) clsdict['_attrs_'] = attrnames return super(OrderedTypeMeta, mcls).__new__(mcls, clsname, bases, clsdict) class User(DataStructure): __metaclass__ = OrderedTypeMeta _attrs_ = (('name', StringValue()), ('password', StringValue()), ('age', IntegerValue())) 

I say this is more general than the django way, because you don't need attributes to be instances of a particular class, any value will do. It is also more general than __slots__ , because you can still assign attributes to class instances (although this might not be necessary: โ€‹โ€‹in this case, I would prefer __slots__ ). In python3, I would prefer __prepare__ .

The main disadvantage of this, besides being ugly, is that it will not work with inheritance. It would not be too difficult to get __attrs__ from the base classes and extend this, instead of setting it to an empty list.

+2
source share

You can use __slots__ to explicitly order attributes. If every class you inherit defines it, there is a bonus performance when using memory and accessing attributes.

Edit: The most reliable way would be to define __prepare__ in the metaclass. The referenced documents are bundled with an example of storing attributes in an OrderedDictionary .

You cannot have order implicitly in Python 2 without the Django field class method, because Python simply does not track this information; it is lost by the time the metaclass appears.

+1
source share

Below I can highlight the order of attributes that are methods or of type struct. I have not tried this with inline types. Then you can build a walker around the kids

  class ordered_type (type):
     __ordernate__ = 0
     def __new __ (meta, name, baseclasses, dct):
         new = super (Type, meta) .__ new __ (meta, name, baseclasses, dct)
         new .__ ordernate__ = Type .__ ordernate__
         Type .__ ordernate__ + = 1        
         def key (x):
             from inspect import ismethod
             if ismethod (x):
                 return x.im_func .__ code __. co_firstlineno
             elif isinstance (x, type):
                 return x .__ ordernate__ + 1000000
         new .__ children__ = sorted (
             [getattr (new, x) for x in dir (new) if not x.startswith ('__')],
             key = key)

 class struct (object):
     __metaclass__ = ordered_type

 #use case
 class A (struct):
     '' 'Description of class A
     '' '
     def name (self):
         '' 'The name of instance of A
         '' '
     class B (struct):
         '' 'Description of class B
         '' '
         def name (self):
             '' 'The name of instance of B
             '' '
0
source share

All Articles