Reading class attributes in the same order as declared in Python
Your exact problem has a solution in Python3 using a metaclass. It's described in the docs.
However, it uses the __prepare__
classmethod, which is not available in Python2.7. After some googling I did find this solution that ports __prepare__
to Python2.7, but it's incredibly hacky and dirty.
To me it sounds like you're having the XY problem, and your problem can be solved through a different approach.
How to read order of declared members in Python class with use reflection or parsing (metaclass replace is forbidden)?
You'll have to resort to parsing. You don't need to write a parser here; the ast
module can do this for you.
Parse the source with ast.parse()
, then walk the resulting tree:
class ClassOrder(ast.NodeVisitor):
identifiers = None
def visit_ClassDef(self, node):
self.identifiers = []
for child in node.body:
if isinstance(child, ast.Assign):
for target in child.targets:
self.visit(target)
elif isinstance(child, ast.FunctionDef):
self.identifiers.append(child.name)
def visit_Name(self, node):
if self.identifiers is not None:
self.identifiers.append(node.id)
tree = ast.parse(sourcecode)
order = ClassOrder()
order.visit(tree)
print order.identifiers
prints out the order of assignments and methods in all class definitions.Demo:
>>> sourcecode = '''\
... class A(object):
... b = 1
... a = 1
... c = 1
... '''
>>> tree = ast.parse(sourcecode)
>>> order = ClassOrder()
>>> order.visit(tree)
>>> print order.identifiers
['b', 'a', 'c']
>>> tree = ast.parse(inspect.getsource(ast.NodeVisitor))
>>> order = ClassOrder()
>>> order.visit(tree)
>>> print order.identifiers
['visit', 'generic_visit']
Bundled up as a function, using inspect.getsource()
:import inspect
def get_identifiers(obj):
source = inspect.getsource(obj)
tree = ast.parse(source)
order = ClassOrder()
order.visit(tree)
return order.identifiers
inspect.getsource()
can handle any object for which the source is available. For classes, it'll extract just the source block defining that class, if available. class attributes declaration, order of the attributes' properties (final, private, static, type)
The language specification only says that modifiers must go before the type, thus int
comes last.
Modifiers include type parameters, annotations, access modifiers (private, protected, public), static
, final
, synchronized
, strictfp
, volatile
, transient
and they (from "what allows the compiler") can come in any order.
Some days ago I did a google search and static final
is much more often than final static
, so this helps ordering them :-)
I think in general this order of the modifiers is most common:
- Annotations
- type parameters
- access modifiers
static
final
transient
(only for fields)volatile
(only for variables)synchronized
(only for methods)
strictfp
or native
, but I think I would put them around synchronized
. How to iterate over a the attributes of a class, in the order they were defined?
I don't think this is possible in Python 2.x. When the class members are provided to the __new__
method they are given as a dictionary, so the order has already been lost at that point. Therefore even metaclasses can't help you here (unless there are additional features that I missed).
In Python 3 you can use the new __prepare__
special method to create an ordered dict (this is even given as an example in PEP 3115).
How to get class properties in order of declaration when serialize
Properties are not guaranteed to have or maintain any specific order in JavaScript. What you will do in your C# code can't change this "limitation".
BTW, the same goes for .net.
Kotlin get property in the same order they are declared
Easier way would be to read properties from the constructor:
ClassWithSortedProperties::class.
primaryConstructor?.
parameters?.
forEachIndexed { i, property ->
println("$i $property")
}
Is the order of declarations within a class important?
It's important for the visibility of class members
class Account{
public:
Account(string firstName, string lastName, int id);
void printAccount();
Foo makeFoo(); // invalid! Foo not declared yet
typedef Foo foo_type; // invalid! Foo not declared yet
private:
string strLastName;
string strFirstName;
class Foo { };
};
You can only refer to a not yet declared name in a parameter, default argument, function body or constructor initialization list in class (or such things in nested classes). In other cases, you can't and you have to arrange declarations such that they are visible. Get an ordered dictionary class attributes inside __init__
You can't do that, because the __init__
attribute called after that the instance has been created (by __new__()
) so if you even override the __new__()
and use a __prepare__
method using a metaclass, you can just get an ordered sequence (dict or etc.) of other methods and attributes which are not defined within __init__
method.
Also based on this mail:
But this doesn't mean that you can't get a list of ordered attributes of your class. Since every attribute sets byIt's just not possible to have something different than a dict as a type's
__dict__
. It's a deliberate limitation and required optimization.
__setattr__
method you can simply preserve your attributes in an ordered dict by overriding the __setattr__
method:from collections import OrderedDict
class NewName:
ordered_attrs = OrderedDict()
def __setattr__(self, name, val):
object.__setattr__(self, name, val)
# Use setattr(self, name, val) if you don't want to preserve the attributes in instances `__dict__`
NewName.ordered_attrs[name] = val
def __init__(self):
# your __init__ body
mynewname = NewName()
print(list(NewName.ordered_attrs))
Output:['Name', 'DecomposedAlias', 'Meaning', 'SIUnit', 'NormalDisplayUnit', 'OrientationConvention', 'ContactPerson', 'Note', 'SubType', 'RefersTo']
# Output of mynewname.__dict__
{'Note': '', 'Meaning': '', 'RefersTo': [], 'SIUnit': OrderedDict([('ScaleFactor', None), ('Offset', None), ('A', None), ('cd', None), ('K', None), ('kg', None), ('m', None), ('mol', None), ('rad', None), ('s', None)]), 'DecomposedAlias': OrderedDict([('Prefix', None), ('Measurement', None), ('Direction', None), ('Item', None), ('Location', None), ('Descriptor', None), ('Frame', None), ('RTorigin', None)]), 'SubType': None, 'Name': None, 'ContactPerson': '', 'NormalDisplayUnit': OrderedDict([('ScaleFactor', None), ('Offset', None), ('A', None), ('cd', None), ('K', None), ('kg', None), ('m', None), ('mol', None), ('rad', None), ('s', None)]), 'OrientationConvention': ''}
Also regarding the setting the attributes, based on documentation:If
__setattr__()
wants to assign to an instance attribute, it should call the base class method with the same name, for example,object.__setattr__(self, name, value)
.
Related Topics
Unexpected Results Converting Timezones in Python
Opencv Python: Draw Minarearect ( Rotatedrect Not Implemented)
Typeerror: Got Multiple Values for Argument
How to Assign the Same Value to Multiple Keys in a Dict Object at Once
Is 'Import Module' Better Coding Style Than 'From Module Import Function'
Loading JSONl File as JSON Objects
How to Get Windows' Special Folders for Currently Logged-In User
What Is the Purpose of Flask's Context Stacks
Is It Better to Use "Is" or "==" for Number Comparison in Python
Most Efficient Way to Reverse a Numpy Array
"Permission Denied" Trying to Run Python on Windows 10
How to Frame Two for Loops in List Comprehension Python
Find the Date for the First Monday After a Given Date
Python Using Variables from Another File
Python, Default Keyword Arguments After Variable Length Positional Arguments