I am writing a metaclass that reads class attributes and store them in a list, but I want the list (cls.columns) to respect the declaration order (that is: mycol2
, mycol3
, zut
, cool
, menfin
, a
in my example):
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)
Result:
[('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>)]
This does not respect the declaration order of Column()
attributes for Listing
class. If I use classDict
directly, it does not help either.
How can I proceed?
An answer that excludes methods:
I guess you should be able to make a class where you replace its
__dict__
with anordered-dict
Here is the workaround I juste developped :
If you are using Python 2.x then you'll need a hack such as the one Lennart proposes. If you are using Python 3.x then read PEP 3115 as that contains an example which does what you want. Just modify the example to only look at your Column() instances:
In the current version of Python, the class ordering is preserved. See PEP520 for details.
In older versions of the language (3.5 and below, but not 2.x), you can provide a metaclass which uses an
OrderedDict
for the class namespace.This approach doesn't help you with existing classes, however, where you'll need to use introspection.
Based on solution by @Duncan, but simpler (for python3 ONLY). We use that fact, that
__prepare__
method returns aOrderDict
instead of simpledict
- so all attributes gathered before__new__
call will be ordered.Now you can use attribute
__fields__
for accessing attributes in required order:Note that there will be attrs
'__module__'
,'__qualname__'
born fromtype
class. To get rid of them you may filter names in following manner (changeOrderedClass.__new__
):it will give only attrs from MyClass:
please remember your answer only workable in python3.x, because there is no prepare definition in python2.7