当前位置: 代码迷 >> python >> 如何在Python中获取类属性的定义顺序?
  详细解决方案

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

热度:105   发布时间:2023-06-13 15:11:42.0

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

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

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

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

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

哦,只是为了清楚,我想要的是一种方法来解决上述定义: ['username', 'password', 'age']

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。

这在Python中根本不受支持。 Django使用元类来处理它。 看到这个问题:

(小结:看django.forms.forms.DeclarativeFieldsMetaclassdjango.forms.forms.get_declared_fields以及如何creation_counter在使用django.forms.fields.Field 。)

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

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__并扩展它而不是将其设置为空列表并不太难。

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

编辑:最可靠的方法是在元类中定义__prepare__ 参考文档具有它, 。

没有Django的字段类技术,你不能在Python 2中隐式地使用顺序,因为Python只是不跟踪那些信息; 它在调用元类时丢失了。

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