如何在Python中获取类属性的定义顺序?

时间:2010-11-18 23:31:00

标签: python oop class data-structures hash

我想定义应该代表数据结构的轻量级类。与许多数据结构的情况一样,数据的顺序很重要。所以如果我继续定义这个:

class User(DataStructure):
    username = StringValue()
    password = StringValue()
    age = IntegerValue()

我暗示这是一个数据结构,其中首先是带有用户名的字符串,后跟带有密码的字符串,最后是用户的年龄作为整数。

如果您熟悉Python,您就会知道上面的类User是继承自type的对象。它就像Python中的大多数其他对象一样具有__dict__。这里存在我的问题。这个__dict__是一个哈希映射,因此__dict__中类属性的顺序与它们的定义顺序无关。

有什么方法可以找出实际的定义顺序吗?在我采用我能想到的一种不那么理智的方法之前,我在这里问...

哦,只是为了清楚,我想要的是一种从上述定义中得到这个的方法:['username', 'password', 'age']

5 个答案:

答案 0 :(得分:5)

Python 2.7和3.x在collections模块中定义OrderedDict。我相信它使用链表来维护其项目的插入顺序。它为标准的可变映射方法添加了可迭代的方法。

您可以定义一个元类,它使用OrderedDict而不是标准的无序dict作为数据结构类的名称空间__dict__。如果您为元类赋予特殊的__prepare__()方法,则可以执行此操作。我没有尝试过,但根据文档可能:

从Python 3.1语言参考部分3.3.3数据模型 - 自定义类创建:

If the metaclass has a __prepare__() attribute (usually implemented as a class 
or static method), it is called before the class body is evaluated with the 
name of the class and a tuple of its bases for arguments. It should return an 
object that supports the mapping interface that will be used to store the 
namespace of the class. The default is a plain dictionary. This could be used, 
for example, to keep track of the order that class attributes are declared in 
by returning an ordered dictionary.

不幸的是,Python 2.7语言参考中的等效部分3.4.3没有提及能够替换类命名空间字典而没有提及__prepare__()方法。所以这可能只适用于Python版本3。

答案 1 :(得分:3)

这在Python中根本不受支持。 Django使用元类来处理它。请参阅此问题:How does Django Know the Order to Render Form Fields?

(摘要:查看django.forms.forms.DeclarativeFieldsMetaclassdjango.forms.forms.get_declared_fields以及creation_counterdjango.forms.fields.Field的使用方式。)

答案 2 :(得分:2)

比Django的方法更通用的一种方法,在python 2中可用的__slots__ 将是这个元类:

class OrderedTypeMeta(type):
    def __new__(mcls, clsname, bases, clsdict):
        attrs = clsdict.get('_attrs_', [])
        attrnames = []
        for name, value in attrs:
            clsdict[name] = value
            attrnames.append(name) 
        clsdict['_attrs_'] = attrnames
        return super(OrderedTypeMeta, mcls).__new__(mcls, clsname, bases, clsdict)


class User(DataStructure):
    __metaclass__ = OrderedTypeMeta
    _attrs_ = (('name', StringValue()),
               ('password', StringValue()),
               ('age', IntegerValue()))

我说它比django的方式更通用,因为你不需要属性作为特定类的实例,任何值都可以。它也比__slots__更通用,因为你仍然可以为类的实例分配属性(虽然这可能不需要:在这种情况下,我更喜欢__slots__)。在python3中,我更喜欢__prepare__

除了它有点丑陋之外,它的主要缺点是它不能用于继承。从基类中获取__attrs__并扩展它而不是将其设置为空列表并不太困难。

答案 3 :(得分:1)

您可以使用__slots__显式排序属性。如果您继承的每个类都定义它,那么内存和属性访问会带来额外的性能提升。

编辑:最可靠的方法是在元类中定义__prepare__。参考文档有complete with an example of storing attributes in an OrderedDictionary

如果没有Django的字段类技术,你不能在Python 2中隐式地使用命令,因为Python不会跟踪那些信息;在调用元类时它会丢失。

答案 4 :(得分:0)

以下是让我捕获属于struct或属性类型的属性的顺序。我还没有尝试过内置类型。然后,您可以围绕孩子

构建一个助行器
class ordered_type(type):
    __ordernate__ = 0
    def __new__(meta, name, baseclasses, dct):
        new = super(Type, meta).__new__(meta, name, baseclasses, dct)
        new.__ordernate__ = Type.__ordernate__
        Type.__ordernate__ += 1        
        def key(x):
            from inspect import ismethod
            if ismethod(x):
                return x.im_func.__code__.co_firstlineno
            elif isinstance(x, type):
                return x.__ordernate__ + 1000000
        new.__children__ = sorted(
            [getattr(new, x) for x in dir(new) if not x.startswith('__')],
            key=key)

class struct(object):
    __metaclass__ = ordered_type

#use case
class A(struct):
    '''Description of class A
    '''
    def name(self):
        '''The name of instance of A
        '''
    class B(struct):
        '''Description of class B
        '''
        def name(self):
            '''The name of instance of B
            '''