10

Python comes with the handy dir() function that would list the content of a class for you. For example, for this class:

class C:
   i = 1
   a = 'b'

dir(C) would return

['__doc__', '__module__', 'a', 'i']

This is great, but notice how the order of 'a' and 'i' is now different then the order they were defined in.

How can I iterate over the attributes of C (potentially ignoring the built-in doc & module attributes) in the order they were defined? For the C class above, the would be 'i' then 'a'.

Addendum: - I'm working on some serialization/logging code in which I want to serialize attributes in the order they were defined so that the output would be similar to the code which created the class.

vaultah
  • 44,105
  • 12
  • 114
  • 143
Boaz
  • 25,331
  • 21
  • 69
  • 77
  • Why would the order they are defined matter at all? Order is irrelevant in this case. How would you use that info if you could get it? – Jason Coon Feb 17 '10 at 13:38
  • Are you serializing the class or instances of the class? In your example, `a` and `i` are class attributes. If you mean these to be instance attributes, you should define them in `__init__` using `self.a = 'b'` and `self.i = 1`. – PaulMcG Feb 17 '10 at 14:06
  • These are class-level attributes, which aren't as useful as instance variables. Why are you trying to preserve the order of class attributes? Wouldn't it be better to preserve the order of instance variables? Why are you trying to do tricks with class-level variables? – S.Lott Feb 17 '10 at 15:14
  • I'm building a generated class-tailored code that gets injected in the class object. It was then a natural choice to enumerate attribute on a class level. If switching to an instance object will make things easier, I would happily switch. – Boaz Feb 17 '10 at 15:46

4 Answers4

8

I don't think this is possible in Python 2.x. When the class members are provided to the __new__ method they are given as a dictionary, so the order has already been lost at that point. Therefore even metaclasses can't help you here (unless there are additional features that I missed).

In Python 3 you can use the new __prepare__ special method to create an ordered dict (this is even given as an example in PEP 3115).

nikow
  • 21,190
  • 7
  • 49
  • 70
  • Here's how Django does it (Python 2.x and up): https://github.com/django/django/blob/stable/1.3.x/django/db/models/fields/__init__.py#L56 – tobych Jan 11 '13 at 01:25
  • I haven't used Django yet, but I guess this requires that in each assignment we create a new `Field` instance? Sounds like a good solution. – nikow Jan 13 '13 at 11:02
  • Yes, that's what Django does. – tobych Jan 15 '13 at 00:23
2

I'm new in python, so my solution may be not effective

class Q:
def __init__(self):
    self.a = 5
    self.b = 10

if __name__ == "__main__":
    w = Q() 
    keys = w.__dict__.keys()
    for k in keys:
        print "Q.%s = %d"%(k,w.__dict__[k])

Output is:

Q.a = 5
Q.b = 10
biv
  • 96
  • 5
2

put this method in your class

def __setattr__(self, attr, value):
    if attr not in dir(self):
        if attr == "__ordered_fields__":
            super.__setattr__(self, attr, value)
        else:
            if not hasattr(self, "__ordered_fields__"):
                setattr(self, "__ordered_fields__", [])
            self.__ordered_fields__.append(attr)
            super.__setattr__(self, attr, value)

and to get the fields in order, just do something like:

print(self.__ordered_fields__)
dmjalund
  • 285
  • 2
  • 9
1

This information is not accessible, since attributes are stored in a dict, which is inherently unordered. Python does not store this information about order anywhere.

If you want your serialization format to store things in a certain order you would specify that order explicitly. You would not use dir, which contains things you don't know or care about, you would explicitly provide a list (or whatever) describing what to serialize.

Mike Graham
  • 73,987
  • 14
  • 101
  • 130