确定Python/IronPython函数的声明顺序

3
我将尝试参加一门Python课程(使用IronPython),并将其应用于一些数据,然后在网格中显示结果。函数的结果成为网格上的列,我希望函数的顺序是列的顺序,也就是按照在源代码中声明的顺序。有没有一种方法可以确定Python类中函数的顺序?无论是使用IronPython还是普通的Python,我都接受回答。
例如,如果我有一个类:
class foo:
    def c(self):
        return 3
    def a(self):
        return 2
    def b(self):
        return 1

我希望能够获得一个列表 [c, a, b],但不想自己解析源代码。有什么建议吗?

需要注意的是,IronPython曾经按照函数声明的顺序保留函数引用,但在.NET 4中,它们改变了这种行为以匹配Python(始终按字母顺序列出函数)。


我确实想知道为什么这被标记为C#。 - Sander Rijken
我曾经使用.NET 3.5和IronPython来完成这个操作,但是他们改变了GetMemberNames()的实现方式,以匹配在.NET 4中的Python dict实现方式列出成员名称。 - Mike Goodspeed
3个回答

2
在Python 2.X中(IronPython目前使用的是Python 2),答案是否定的。Python在创建类对象之前会构建一个类成员的字典。一旦类被创建,就没有“记录”了顺序。
在Python 3中,元类(用于创建类)得到了改进,您可以使用自定义对象而不是标准字典来收集类成员,以便在构建类时知道其顺序。
然而,对于IronPython,有一个可能起作用的hack。Python字典本质上是无序的,即遍历字典的成员会以任意(但稳定)的顺序返回它们。在IronPython中,由于实现的偶然性,迭代会按照插入的顺序返回成员。(请注意,这可能不再是事实。)
您可以尝试:
 members = list(c.__dict__) # or c.__dict__.keys()

你可能会发现这些顺序符合你的期望。但是,运行不同版本的IronPython(或其他Python实现)的代码可能会以不同的顺序返回它们。

另一个更好但更难的方法是使用ast模块(或IronPython解析器)并遍历AST来查找条目。虽然不是很困难,但需要更多的工作。


你确定可以这样使用元类吗?我对它们不太熟悉,但我认为Python会首先将类定义解析为字典,然后将其作为参数传递给元类进行构建。但我很想看到我错了! - Katriel
在Python 3中,元类可以实现一个__prepare__函数,用于创建新类命名空间的字典 - 请参阅http://www.python.org/dev/peps/pep-3115/。 - Jochen Ritzel
谢谢!我最终使用子类化IronPython的PythonWalker来理解类的结构(覆盖Walk()和PostWalk())。现在我正在这样做,可能会得到比我想要的更多的函数(在嵌套函数的情况下),但这不会影响我的特定应用程序。 - Mike Goodspeed
很棒!而且,PEP中的示例确实就是OP想要的。 - Katriel

2

为什么你不能自己解析代码?这很简单:

import inspect
import ast

class Foo:
    def c(self):
        pass

    def a(self):
        pass

    def b(self):
        pass

code = ast.parse(inspect.getsource(Foo))

class FunctionVisitor(ast.NodeVisitor):
    def visit_FunctionDef(self, node):
        print(node.name)

FunctionVisitor().visit(code)

输出

c
a
b

这非常有帮助!我不想自己解析代码,但是通过语法树进行步进也不错。 - Mike Goodspeed

1
另一种解决方案是使用装饰器将它们添加到列表中。
def make_registry():
    def register(f):
        register.funcs.append(f)
        return f
    register.funcs = []
    return register

register = make_registery()

class Foo(object):

    @register
    def one(self):
        pass

    @register
    def two(self):
        pass

    @register
    def three(self):
        pass

    def utility_method_that_shouldnt_be_run_in_the_sequence(self):
        pass

print register.funcs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接