我想定义应该代表数据结构的轻量级类。与许多数据结构的情况一样,数据的顺序很重要。所以如果我继续定义这个:
class User(DataStructure):
username = StringValue()
password = StringValue()
age = IntegerValue()
我暗示这是一个数据结构,其中首先是带有用户名的字符串,后跟带有密码的字符串,最后是用户的年龄作为整数。
如果您熟悉Python,您就会知道上面的类User
是继承自type
的对象。它就像Python中的大多数其他对象一样具有__dict__
。这里存在我的问题。这个__dict__
是一个哈希映射,因此__dict__
中类属性的顺序与它们的定义顺序无关。
有什么方法可以找出实际的定义顺序吗?在我采用我能想到的一种不那么理智的方法之前,我在这里问...
哦,只是为了清楚,我想要的是一种从上述定义中得到这个的方法:['username', 'password', 'age']
答案 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.DeclarativeFieldsMetaclass
,django.forms.forms.get_declared_fields
以及creation_counter
中django.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 '''