如何读取与声明相同的顺序的类属性?

我正在写一个读取类属性并将它们存储在列表中的元类,但是我希望列表(cls.columns)遵守声明顺序(即: mycol2mycol3zutcoolmenfin ,在我的示例中) :

 import inspect import pprint class Column(object): pass class ListingMeta(type): def __new__(meta, classname, bases, classDict): cls = type.__new__(meta, classname, bases, classDict) cls.columns = inspect.getmembers(cls, lambda o: isinstance(o, Column)) cls.nb_columns = len(cls.columns) return cls class Listing(object): __metaclass__ = ListingMeta mycol2 = Column() mycol3 = Column() zut = Column() cool = Column() menfin = Column() a = Column() pprint.pprint(Listing.columns) 

结果:

 [('a', <__main__.Column object at 0xb7449d2c>), ('cool', <__main__.Column object at 0xb7449aac>), ('menfin', <__main__.Column object at 0xb7449a8c>), ('mycol2', <__main__.Column object at 0xb73a3b4c>), ('mycol3', <__main__.Column object at 0xb744914c>), ('zut', <__main__.Column object at 0xb74490cc>)] 

这不符合Listing类的Column()属性的声明顺序。 如果我直接使用classDict ,它也没有帮助。

我如何继续?

在当前版本的Python中,类的顺序是保留的。 有关详细信息,请参阅PEP520 。

在旧版本的语言(3.5及以下,但不是2.x)中,您可以提供一个使用OrderedDict作为类名称空间的元类。

 import collections class OrderedClassMembers(type): @classmethod def __prepare__(self, name, bases): return collections.OrderedDict() def __new__(self, name, bases, classdict): classdict['__ordered__'] = [key for key in classdict.keys() if key not in ('__module__', '__qualname__')] return type.__new__(self, name, bases, classdict) class Something(metaclass=OrderedClassMembers): A_CONSTANT = 1 def first(self): ... def second(self): ... print(Something.__ordered__) # ['A_CONSTANT', 'first', 'second'] 

这种方法对现有的类没有什么帮助,但是在那里你需要使用内省。

这是我开发的解决方法:

 import inspect class Column(object): creation_counter = 0 def __init__(self): self.creation_order = Column.creation_counter Column.creation_counter+=1 class ListingMeta(type): def __new__(meta, classname, bases, classDict): cls = type.__new__(meta, classname, bases, classDict) cls.columns = sorted(inspect.getmembers(cls,lambda o:isinstance(o,Column)),key=lambda i:i[1].creation_order) cls.nb_columns = len(cls.columns) return cls class Listing(object): __metaclass__ = ListingMeta mycol2 = Column() mycol3 = Column() zut = Column() cool = Column() menfin = Column() a = Column() for colname,col in Listing.columns: print colname,'=>',col.creation_order 

对于python 3.6,这已成为默认行为。 请参阅PEP520: https ://www.python.org/dev/peps/pep-0520/

 class OrderPreserved: a = 1 b = 2 def meth(self): pass print(list(OrderPreserved.__dict__.keys())) # ['__module__', 'a', 'b', 'meth', '__dict__', '__weakref__', '__doc__'] 

如果你使用的是Python 2.x,那么你需要一个像Lennart所提议的那样的黑客。 如果您使用的是Python 3.x,请阅读PEP 3115 ,其中包含一个您想要的示例。 只需修改示例以查看您的Column()实例:

  # The custom dictionary class member_table(dict): def __init__(self): self.member_names = [] def __setitem__(self, key, value): # if the key is not already defined, add to the # list of keys. if key not in self: self.member_names.append(key) # Call superclass dict.__setitem__(self, key, value) # The metaclass class OrderedClass(type): # The prepare function @classmethod def __prepare__(metacls, name, bases): # No keywords in this case return member_table() # The metaclass invocation def __new__(cls, name, bases, classdict): # Note that we replace the classdict with a regular # dict before passing it to the superclass, so that we # don't continue to record member names after the class # has been created. result = type.__new__(cls, name, bases, dict(classdict)) result.member_names = classdict.member_names return result class MyClass(metaclass=OrderedClass): # method1 goes in array element 0 def method1(self): pass # method2 goes in array element 1 def method2(self): pass 

基于@Duncan的解决scheme,但更简单( 仅适用于python3 )。 我们使用这个事实, OrderDict方法返回一个OrderDict而不是简单的dict – 因此在__new__调用之前收集的所有属性将被sorting。

 from collections import OrderedDict class OrderedClass(type): @classmethod def __prepare__(mcs, name, bases): return OrderedDict() def __new__(cls, name, bases, classdict): result = type.__new__(cls, name, bases, dict(classdict)) result.__fields__ = list(classdict.keys()) return result class Column: pass class MyClass(metaclass=OrderedClass): mycol2 = Column() mycol3 = Column() zut = Column() cool = Column() menfin = Column() a = Column() 

现在,您可以使用属性__fields__以所需顺序访问属性:

 m = MyClass() print(m.__fields__) ['__module__', '__qualname__', 'mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a'] 

请注意,将会有type'__qualname__' attrs '__module__''__qualname__' 。 为了摆脱它们,你可以按以下方式过滤名称(更改OrderedClass.__new__ ):

 def __new__(cls, name, bases, classdict): result = type.__new__(cls, name, bases, dict(classdict)) exclude = set(dir(type)) result.__fields__ = list(f for f in classdict.keys() if f not in exclude) return result 

它只会给MyClass的attrs:

 ['mycol2', 'mycol3', 'zut', 'cool', 'menfin', 'a'] 

请记住你的答案只能在python3.x中使用,因为python2.7中没有准备定义

排除方法的答案:

 from collections import OrderedDict from types import FunctionType class StaticOrderHelper(type): # Requires python3. def __prepare__(name, bases, **kwargs): return OrderedDict() def __new__(mcls, name, bases, namespace, **kwargs): namespace['_field_order'] = [ k for k, v in namespace.items() if not k.startswith('__') and not k.endswith('__') and not isinstance(v, (FunctionType, classmethod, staticmethod)) ] return type.__new__(mcls, name, bases, namespace, **kwargs) class Person(metaclass=StaticOrderHelper): first_name = 'First Name' last_name = 'Last Name' phone_number = '000-000' @classmethod def classmethods_not_included(self): pass @staticmethod def staticmethods_not_included(self): pass def methods_not_included(self): pass print(Person._field_order) 

我想你应该能够做一个类,你用一个ordered-dictreplace__dict__