2

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.

Community
  • 1
  • 1
fma
  • 219
  • 2
  • 14

1 Answers1

1

You can't do that, because the __init__ attribute called after that 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).

Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • thanks, now I can retrieve the ordered dictionary. However after your suggested modification of the class, if I ask `mynewdict = mynewname.__dict__` it returns an empty dictionary. Do you know why is that? – fma Sep 16 '16 at 13:57
  • @Francesco In that case, if you want to have the attributes in instance's `__dict__` you should use `object.__setattr__(self, name, val)` in order to set the attributes. – Mazdak Sep 16 '16 at 14:16
  • thanks for the edit. However I am not really an expert of python and I am afraid I have not understood what you mean. In my class I added a function like: `@staticmethod def ordered_dict(): return ordered_attrs` so to retrieve the ordered dictionary with `mynewdict = mynewname.ordered_dict()`. How should I use `object.__setattr__(self, name, val)` to get an unordered dictionary, as I obtained with `mynewdict = mynewname.__dict__`? In other words, after the proposed modification to the class, can I still get in some way an unordered dictionary? – fma Sep 16 '16 at 14:30
  • @Francesco You don't need to use `object.__setattr__(self, name, val)` to get an unordered dictionary. After the modification you can get the unordered-dictionary with `mynewname.__dict__`. – Mazdak Sep 16 '16 at 14:48
  • The problem is that now with `mynewdict = mynewname.__dict__` I obtain an empty dictionary and I am not understanding why. – fma Sep 16 '16 at 15:03
  • @Francesco Are you sure? because it works fine for me in python 3 and 2. – Mazdak Sep 16 '16 at 15:06
  • Why not just `NewName.ordered_attrs[name] = val` and `print(NewName.ordered_attrs)`? What is the point of using global when it is a class attribute? – Padraic Cunningham Sep 16 '16 at 15:12
  • This would also error if the init took arguments when you called i.e `def __init__(self, foo)` `self.foo = foo` – Padraic Cunningham Sep 16 '16 at 15:19
  • @PadraicCunningham Yes, that's the correct way, I used a global for test, and just forgot to change it. Regarding the error by passing argument to `__init__` I don't agree because it don't. What kind of error you've got? – Mazdak Sep 16 '16 at 15:27
  • Thanks @kasramvd I have reviewed my code with your edit and now everything works fine. – fma Sep 19 '16 at 11:52
  • @Francesco Thank you for asking this interesting question. ;-) – Mazdak Sep 19 '16 at 13:24