Get the class method in the order in which it was in code

This code:

import inspect class Obj(): def c(self): return 1 def b(self): return 2 def a(self): return 3 o = Obj() for name, value in inspect.getmembers(o, inspect.ismethod): print str(value())+" "+name 

Print

 3 a 2 b 1 c 

Because of inspect.getmembers return all members of the object to a list of pairs (name, value) sorted by name, as you can read at https://docs.python.org/2/library/inspect.html#inspect.getmembers

But I want to get this list in the same order as the members written in the code, in other words, the output will be:

 1 c 2 b 3 a 

Can this be done?

thanks

+5
source share
4 answers

When creating an object, all its attributes are contained in another specialized attribute in the object named __dict__ , which, as the name implies, is a regular unordered Python dictionary, so they are not guaranteed to be added in the same way. When retrieving values ​​in __dict__ using getmembers() , Python automatically reorganizes the dictionary when printed to make some logical sense.

To combat this, you need to do something to turn a regular Python __dict__ into some sort of ordered one.

This can be done in several ways, for simplicity I assume that you are using Python 3.

Using the collections package, you can get OrderedDict , which is exactly the technology that we need for such a problem. Prepare this ordered dictionary for use in the metaclass for the class to be stored for ordered members, copy it, and finally access this new OrderedDict when you want to print the specified members.

This can be seen in action in this answer .

+1
source

In cpython code cpython to bytecode for the virtual machine. And functions have the __code__ attribute, which is a code object. The code object has the co_firstlineno attribute, which is the first line of Python source code. (Details in inspect .)

If you know that your methods are all in source code, and you know that you are using cpython, you can use this as a sort key. But it seems like a terrible wobble if you don't know it.

 members = [ (name,meth) for name, meth in inspect.getmembers(o, inspect.ismethod)] members = sorted(members, key=lambda t: t[1].__code__.co_firstlineno) print '\n'.join(m[0] for m in members) 
+1
source

Hm, these are very hacks, but basically I directly check the source and use re to search for method names. This solution is rather fragile, but it does not concern inheritance, but perhaps it works for you. Assuming I saved the class definition in a file called test.py :

 >>> import test >>> import re >>> findmethods = re.compile(r" def (.+)\(") >>> findmethods.findall(inspect.getsource(test.Obj)) ['c', 'b', 'a'] >>> 
0
source

No. Class members are not ordered. They are compiled into a dictionary, immediately losing order. You can resort to tricks, for example, disassemble the source, but it will easily break. For starters, the source may not be available.

[edit: it seems that python3 provides more flexibility in creating the class, allowing you to customize the way you collect class members , if you are only on python3, this is probably the best approach]

If changing the code is not a problem, you can use the decorator:

 import inspect def track_order(fn): fn.order = track_order.idx track_order.idx += 1 return fn track_order.idx = 0 class Obj(object): @track_order def c(self): return 1 @track_order def b(self): return 2 @track_order def a(self): return 3 o = Obj() methods = sorted((item for item in inspect.getmembers(o, inspect.ismethod)), key=lambda item: item[1].order) for name, value in methods: print str(value())+" "+name 

The decorator adds the idx attribute to all methods that pass through it. This exploits the fact that python has first-class features.

 $ python test.py 1 c 2 b 3 a 

Note. This is the method used by Django to track the order of forms and model fields. Only they do not need a decorator, because field classes have a built-in instanciation order attribute (called creation_counter ).

0
source

Source: https://habr.com/ru/post/1266428/


All Articles