代码之家  ›  专栏  ›  技术社区  ›  Blixt

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

  •  6
  • Blixt  · 技术社区  · 14 年前

    我想定义轻量级的类来表示数据结构。与许多数据结构一样,数据的顺序非常重要。所以如果我继续定义这个:

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

    我的意思是,这是一个数据结构,首先是一个用户名字符串,然后是一个密码字符串,最后是一个整数形式的用户年龄。

    如果你熟悉Python,你就会知道上面的类, User ,是继承自 type . 它将像Python中的大多数其他对象一样,具有 __dict__ . 我的问题就在这里。这个 __迪克特__ 是散列映射,因此 __迪克特__ 与他们的定义顺序毫无关系。

    我有什么办法可以计算出实际的定义顺序吗?在我用一种我能想到的不那么理智的方法之前,我先问一下。。。

    哦,我想说的是,我想要的是一种从上述定义中解脱出来的方法: ['username', 'password', 'age']

    5 回复  |  直到 14 年前
        1
  •  3
  •   Community kfsone    7 年前

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

    (小结:看 django.forms.forms.DeclarativeFieldsMetaclass , django.forms.forms.get_declared_fields 以及如何 creation_counter 用于 django.forms.fields.Field .)

        2
  •  5
  •   Don O'Donnell    14 年前

    Python 2.7和3.x定义了 OrderedDict 在“集合”模块中。我相信它使用一个链接列表来维护其项的插入顺序。它将iterable方法添加到标准可变映射方法中。

    您可以定义一个元类,它使用 有序的信息技术 而不是标准的无序 dict 作为命名空间 __dict__ 用于数据结构类。如果你给你的元类一个特殊的 __prepare__() 方法你可以这样做。我还没试过,但根据医生的说法:

    从Python3.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.
    

    不幸的是,Python2.7语言引用中的等效部分3.4.3没有提到能够替换类名称空间dict,也没有提到 __准备 方法。所以这可能只有在Python版本3中才可能实现。

        3
  •  2
  •   aaronasterling    14 年前

    有一种方法比Django的方法更普遍, __slots__ Python2中提供的是这个元类:

    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的方法更通用,因为您不需要将属性作为特定类的实例,任何值都可以。它也比 __插槽__ 因为您仍然可以将属性分配给类的实例(尽管这可能不是必需的:在这种情况下,我更希望 __插槽__ ). 在蟒蛇3号,我更喜欢 __prepare__ .

    这种方法的主要缺点是,除了它有点难看之外,它不会与继承一起工作。不难得到 __attrs__ 扩展基类,而不是将其设置为空列表。

        4
  •  1
  •   Tobu    14 年前

    你可以用 __slots__ 对属性进行显式排序。如果从中继承的每个类都定义了它,那么在内存和属性访问上会有额外的性能提升。

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

    如果没有Django的字段类技术,Python 2中就不能隐式地拥有顺序,因为Python只是不跟踪该信息;当调用元类时,它就会丢失。

        5
  •  0
  •   sleepycat    11 年前

    下面让我捕获方法或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
                '''