I am trying to use an 'ordered class' as described in PEP 3115 (that is, a class whose members can be accessed in the order they were declared). The implementation given there is
# 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
There are a few things I am confused about. Firstly, is there a reason why __prepare__
is a classmethod
? The definition doesn't use metacls
- is this just a convention?
Secondly, when I try this code, '__module__'
ends up in MyClass.member_names
before 'method1'
and 'method2'
, apparently contradicting the comments which claim 'method1'
is the first element. Why does this special attribute end up in the list while no others do? Are there any others that might surprise me (apart from __doc__
if the class has a docstring, and any I define explicitly)?
Finally, this implementation does not retrieve the member_names
from base classes. If I want to achieve that, is there anything wrong with the following change to __prepare__
(apart from the fact that it doesn't check for duplicates)?
@classmethod
def __prepare__(metacls, name, bases):
prep_dict = member_table()
for base in bases:
try:
prep_dict.member_names.extend(base.member_names)
except AttributeError:
pass
return prep_dict