4

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?

Nicolas Dumazet
  • 7,147
  • 27
  • 36
molicule
  • 5,481
  • 3
  • 29
  • 40
  • Why would you want to do this? – David Locke Mar 12 '09 at 21:57
  • 2
    django's forms and model declarations attempt to go by the order in which the entries were made. The attributes however are instances as opposed to class declarations. I'm trying to figure out if class declarations can be so ordered as this would help in auto-template generation. – molicule Mar 12 '09 at 22:13
  • For a odd question like this it's probably better to explain why you want to do what you're trying to do and add a django tag. – David Locke Mar 12 '09 at 22:15

6 Answers6

8

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
  • 116,865
  • 28
  • 107
  • 112
5

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
  • 14,557
  • 2
  • 27
  • 32
5

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.

Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
  • You may also use the `getsourcelines` function, which returns only the object's source lines and the starting line number in a tuple. – Andreas Kraft Dec 09 '22 at 22:20
1

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
  • 148,955
  • 89
  • 346
  • 502
1
class ExampleObject:
    def example2():
        pass
    
    def example1():
        pass

context = ExampleObject

def sort_key(item):
    return inspect.findsource(item)[1]

properties = [
    getattr(context, attribute) for attribute in dir(context)
    if callable(getattr(context, attribute)) and 
    attribute.startswith('__') is False
]
properties.sort(key=sort_key)

print(properties)

Should print out:

[<function ExampleObject.example2 at 0x7fc2baf9e940>, <function ExampleObject.example1 at 0x7fc2bae5e790>]

I needed to use this as well for some compiler i'm building, and this proved very useful.

0

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
  • 719
  • 1
  • 8
  • 11