Get method of a class in the order that it was in the code

时间:2017-04-06 17:21:05

标签: python inspect members

This code:

import inspect

class Obj():

    def c(self):
        return 1

    def b(self):
        return 2

    def a(self):
        return 3

o = Obj()

for name, value in inspect.getmembers(o, inspect.ismethod):
    print str(value())+" "+name

print:

3 a
2 b
1 c

Because of inspect.getmembers return all the members of an object in a list of (name, value) pairs sorted by name, as you can read in https://docs.python.org/2/library/inspect.html#inspect.getmembers

But I want to get that list in the same order that the members was written in the code, in other words, the output would be:

1 c
2 b
3 a

Is any way to do that?

Thanks

4 个答案:

答案 0 :(得分:1)

创建对象时,它的所有属性都包含在名为__dict__的对象中的另一个专用属性中,顾名思义它只是一个普通的Python非有序字典,因此它们不能保证是以相同的方式存储它们。当使用__dict__getmembers()中检索值时,Python会在打印时自动重新组织字典,以便具有某种逻辑意义。

要解决此问题,必须采取措施将常规Python字典__dict__转换为某种有序字典。

这可以通过多种方式完成,为简单起见,我假设您使用的是Python 3。

使用collections包,您可以获得OrderedDict,这正是我们针对此类问题所需的技术。准备这个有序字典,用于需要存储有序成员的类的元类,复制成员,最后在想要打印出所述成员时访问这个新OrderedDict

这可以在this Stack Overflow answer中看到。

答案 1 :(得分:1)

cpython中,代码被编译为VM的字节码。这些函数有一个__code__属性,它是一个代码对象。代码对象具有co_firstlineno属性,这是Python源代码中的第一行。 (详见inspect模块。)

如果您知道您的方法都是源代码,并且您知道使用的是cpython,则可以将其用作排序键。但如果你不了解这些事情,那似乎很不稳定。

members = [ (name,meth) for name, meth in inspect.getmembers(o, inspect.ismethod)]

members = sorted(members, key=lambda t: t[1].__code__.co_firstlineno)

print '\n'.join(m[0] for m in members)

答案 2 :(得分:0)

嗯,这非常hacky,但基本上我直接检查源并使用re来查找方法名称。但是,这个解决方案非常脆弱,并且它不涉及继承,但它可能适合您。假设我已将类定义保存在名为test.py的文件中:

>>> import test
>>> import re
>>> findmethods = re.compile(r"    def (.+)\(")
>>> findmethods.findall(inspect.getsource(test.Obj))
['c', 'b', 'a']
>>>

答案 3 :(得分:0)

没有。班级成员不订购。他们被收集到字典中,立即失去秩序。您可以采用解析源代码等技巧,但它很容易破解。对于初学者来说,来源无法使用。

[编辑:似乎python3在类创建方面允许更多的灵活性,使customize the way class members are gathered成为可能,如果你只使用python3,那可能是更好的方法]

如果更改代码不是问题,您可以使用装饰器:

import inspect

def track_order(fn):
    fn.order = track_order.idx
    track_order.idx += 1
    return fn
track_order.idx = 0

class Obj(object):
    @track_order
    def c(self):
        return 1

    @track_order
    def b(self):
        return 2

    @track_order
    def a(self):
        return 3

o = Obj()

methods = sorted((item
                  for item in inspect.getmembers(o, inspect.ismethod)),
                 key=lambda item: item[1].order)

for name, value in methods:
    print str(value())+" "+name

装饰器为通过它的所有方法添加idx属性。 这利用了python具有一流功能的事实。

$ python test.py
1 c
2 b
3 a

注意:这是Django用来跟踪表单和模型字段顺序的方法。只是,他们不需要装饰,因为领域'类具有内置的instanciation order属性(名为creation_counter)。