inspect.getmembers 按顺序获取?

29 投票
6 回答
6283 浏览
提问于 2025-04-16 00:45
inspect.getmembers(object[, predicate])

返回一个对象的所有成员,以 (名称, 值) 的形式列出,并按名称排序。

我想用这个方法,但我不想让成员按顺序排列。我希望它们按照定义时的顺序返回。有没有其他方法可以做到这一点?


我的需求是创建一个这样的表单:

class RegisterForm(Form):
    username = Field(model_field='username', filters=validators.minlength(3))
    password1 = Field(model_field='password', widget=widgets.PasswordInput)
    password2 = Field(widget=widgets.PasswordInput)
    first_name = Field(model_field='first_name')
    last_name = Field(model_field='last_name')
    address = SubForm(form=AddressForm, model_field='address')

我希望字段按照定义的顺序显示。

6 个回答

5

我觉得Python 2.6没有一个叫做__prepare__的方法,所以我不能把默认的dict换成有顺序的字典。不过,我可以通过使用metaclass__new__方法来替换它。与其检查行号,我觉得用一个创建计数器会更简单、更高效。

class MetaForm(type):
    def __new__(cls, name, bases, attrs):
        attrs['fields'] = OrderedDict(
            sorted(
                [(name, attrs.pop(name)) for name, field in attrs.items() if isinstance(field, Field)],
                key=lambda t: t[1].counter
            )
        )
        return type.__new__(cls, name, bases, attrs)

class Form(object):
    __metaclass__ = MetaForm

class Field(object):
    counter = 0
    def __init__(self):
        self.counter = Field.counter
        Field.counter += 1
5

一个对象的属性(方法和其他成员)通常是通过对象的一个特殊属性 __dict__ 来查找的,这个属性其实就是一个标准的 Python 字典。需要注意的是,它并不保证属性的顺序。

如果在对象的 __dict__ 中找不到某个属性,就会去查找这个属性所在类的 __dict__(通常方法会在这里),然后继续向上查找,直到整个继承链都被遍历一遍。

下面是一些在交互式提示符下进行的自定义检查示例(Python 3.1):

>>> class Klass():
...     def test(self):
...             pass
...
>>> k = Klass()
>>> k.__dict__
{}
>>> k.__class__.__dict__.items()
[('test', <function test at 0x00000000024113C8>), ('__dict__', <attribute '__dic
t__' of 'Klass' objects>), ('__module__', '__main__'), ('__weakref__', <attribut
e '__weakref__' of 'Klass' objects>), ('__doc__', None)]

如果我在 Klass 中放了一个构造函数(__init__),并通过 self 设置了一个属性,那么这个属性就会出现在 k.__dict__ 中。

你可以通过使用自定义元类来绕过这个限制。文档中有一个示例,正好可以实现你想要的功能。

可以查看这个页面底部的 OrderedClass 示例

我不知道你用的是什么版本的 Python,所以假设你用的是最新的版本。

16

你可以去查找一下方法的行号,不太确定其他成员的情况:

import inspect

class A:
    def one(self):
        pass

    def two(self):
        pass

    def three(self):
        pass

    def four(self):
        pass

def linenumber_of_member(m):
    try:
        return m[1].__func__.__code__.co_firstlineno
    except AttributeError:
        return -1

a = A()
l = inspect.getmembers(a)
print(l)
l.sort(key=linenumber_of_member)
print(l)

打印结果是:

[('__doc__', None), ('__module__', '__main__'), ('four', <bound method A.four of <__main__.A instance at 0x0179F738>>), ('one', <bound method A.one of <__main__.A instance at 0x0179F738>>), ('three', <bound method A.three of <__main__.A instance at 0x0179F738>>), ('two', <bound method A.two of <__main__.A instance at 0x0179F738>>)]
[('__doc__', None), ('__module__', '__main__'), ('one', <bound method A.one of <__main__.A instance at 0x0179F738>>), ('two', <bound method A.two of <__main__.A instance at 0x0179F738>>), ('three', <bound method A.three of <__main__.A instance at 0x0179F738>>), ('four', <bound method A.four of <__main__.A instance at 0x0179F738>>)]

撰写回答