views:

1107

answers:

5

The following code

import types
class A:
    class D:
        pass
    class C:
        pass
for d in dir(A):
    if type(eval('A.'+d)) is types.ClassType:
        print d

outputs

C
D

How do I get it to output in the order in which these classes were defined in the code? I.e.

D
C

Is there any way other than using inspect.getsource(A) and parsing that?

+1  A: 

AFAIK, no -- there isn't*. This is because all of a class's attributes are stored in a dictionary (which is, as you know, unordered).

*: it might actually be possible, but that would require either decorators or possibly metaclass hacking. Do either of those interest you?

David Wolever
+5  A: 

No, you can't get those attributes in the order you're looking for. Python attributes are stored in a dict (read: hashmap), which has no awareness of insertion order.

Also, I would avoid the use of eval by simply saying

if type(getattr(A, d)) is types.ClassType:
    print d

in your loop. Note that you can also just iterate through key/value pairs in A.__dict__

zweiterlinde
Yea, +1 for using getattr instead of eval.
David Wolever
A: 

I'm not trying to be glib here, but would it be feasible for you to organize the classes in your source alphabetically? i find that when there are lots of classes in one file this can be useful in its own right.

Atilio Jobson
+5  A: 

Note that that parsing is already done for you in inspect - take a look at inspect.findsource, which searches the module for the class definition and returns the source and line number. Sorting on that line number (you may also need to split out classes defined in separate modules) should give the right order.

However, this function doesn't seem to be documented, and is just using a regular expression to find the line, so it may not be too reliable.

Another option is to use metaclasses, or some other way to either implicitly or explicitly ordering information to the object. For example:

import itertools, operator

next_id = itertools.count().next

class OrderedMeta(type):
    def __init__(cls, name, bases, dct):
        super(OrderedMeta, cls).__init__(name, bases, dct)
        cls._order = next_id()

# Set the default metaclass
__metaclass__ = OrderedMeta

class A:
    class D:
        pass
    class C:
        pass

print sorted([cls for cls in [getattr(A, name) for name in dir(A)] 
           if isinstance(cls, OrderedMeta)], key=operator.attrgetter("_order"))

However this is a fairly intrusive change (requires setting the metaclass of any classes you're interested in to OrderedMeta)

Brian
I liked this approach, but I went with using inspect.findsource and chose MizardX's answer
molicule
+3  A: 

The inspect module also has the findsource function. It returns a tuple of source lines and line number where the object is defined.

>>> import inspect
>>> import StringIO
>>> inspect.findsource(StringIO.StringIO)[1]
41
>>>

The findsource function actually searches trough the source file and looks for likely candidates if it is given a class-object.

Given a method-, function-, traceback-, frame-, or code-object, it simply looks at the co_firstlineno attribute of the (contained) code-object.

MizardX