Francesco Francesco - 1 month ago 20
Python Question

Get an ordered dictionary class attributes inside __init__

I have the following class:

class NewName:
def __init__(self):
self.Name = None
self.DecomposedAlias = OrderedDict([("Prefix", None),
("Measurement", None),
("Direction", None),
("Item", None),
("Location", None),
("Descriptor", None),
("Frame", None),
("RTorigin", None)])
self.Meaning = ""
self.SIUnit = OrderedDict([("ScaleFactor", None),
("Offset", None),
("A", None),
("cd", None),
("K", None),
("kg", None),
("m", None),
("mol", None),
("rad", None),
("s", None)])
self.NormalDisplayUnit = OrderedDict([("ScaleFactor", None),
("Offset", None),
("A", None),
("cd", None),
("K", None),
("kg", None),
("m", None),
("mol", None),
("rad", None),
("s", None)])
self.OrientationConvention = ""
self.ContactPerson = ""
self.Note = ""
self.SubType = None
self.RefersTo = []


If I instantiate a new object of this class I can obtain a dictionary like this:

mynewname = NewName()
mynewdict = mynewname.__dict__


What if I want
mynewdict
to be ordered in the same way the attributes of
NewName
were instantiated in its
__init__
?

Doing some research I found this, but in my case I would just obtain
['__init__']
. Is there a way to point to the attributes inside the
__init__
?

For completeness sake I should mention that I am using Python 3.4.

Answer

You can't do that, because the __init__ attribute called after the instance has been created (by __new__()) so if you even override the __new__() and use a __prepare__ method using a metaclass, you can just get an ordered sequence (dict or etc.) of other methods and attributes which are not defined within __init__ method.

Also based on this mail:

It's just not possible to have something different than a dict as a type's __dict__. It's a deliberate limitation and required optimization.

But this doesn't mean that you can't get a list of ordered attributes of your class. Since every attribute sets by __setattr__ method you can simply preserve your attributes in an ordered dict by overriding the __setattr__ method:

from collections import OrderedDict

class NewName:
    ordered_attrs = OrderedDict()
    def __setattr__(self, name, val):
        object.__setattr__(self, name, val)
        # Use setattr(self, name, val) if you don't want to preserve the attributes in instances `__dict__`
        NewName.ordered_attrs[name] = val

    def __init__(self):
        # your __init__ body

mynewname = NewName()
print(list(NewName.ordered_attrs))

Output:

['Name', 'DecomposedAlias', 'Meaning', 'SIUnit', 'NormalDisplayUnit', 'OrientationConvention', 'ContactPerson', 'Note', 'SubType', 'RefersTo']

# Output of mynewname.__dict__
{'Note': '', 'Meaning': '', 'RefersTo': [], 'SIUnit': OrderedDict([('ScaleFactor', None), ('Offset', None), ('A', None), ('cd', None), ('K', None), ('kg', None), ('m', None), ('mol', None), ('rad', None), ('s', None)]), 'DecomposedAlias': OrderedDict([('Prefix', None), ('Measurement', None), ('Direction', None), ('Item', None), ('Location', None), ('Descriptor', None), ('Frame', None), ('RTorigin', None)]), 'SubType': None, 'Name': None, 'ContactPerson': '', 'NormalDisplayUnit': OrderedDict([('ScaleFactor', None), ('Offset', None), ('A', None), ('cd', None), ('K', None), ('kg', None), ('m', None), ('mol', None), ('rad', None), ('s', None)]), 'OrientationConvention': ''}

Also regarding the setting the attributes, based on documentation:

If __setattr__() wants to assign to an instance attribute, it should call the base class method with the same name, for example, object.__setattr__(self, name, value).