如何按声明的顺序读取类属性?
我正在写一个元类,这个元类会读取类的属性并把它们存储在一个列表里。不过,我希望这个列表(也就是 cls.columns
)能保持属性声明的顺序(比如在我的例子中是:mycol2
、mycol3
、zut
、cool
、menfin
、a
)。
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>)]
这样做并没有保持 Column()
属性在 Listing
类中的声明顺序。如果我直接使用 classDict
,也没有什么帮助。
我该怎么做呢?
7 个回答
16
在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__']
16
这是我刚刚开发的一个解决方法:
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
40
在当前版本的Python中,类的顺序是被保留的。想了解更多,可以查看PEP520。
在旧版本的Python(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']
不过,这种方法对已经存在的类没有帮助,你需要使用反射来处理。