Unfortunately, func_code.co_names
unlikely to help many. This contains all the names that are available in the code segment, including global variables, in order of appearance.
class Test(object): def calc_a(self): return self.b + self.c def calc_x(self): return self.ya + self.yb >>> Test.calc_a.func_code.co_names ('b', 'c') >>> Test.calc_x.func_code.co_names ('y', 'a', 'b')
It is impossible to specify from this array if "a" and "b" are loaded from "self" or from "self.y". Typically, the only way to find out the access pattern for small code without executing it is to disassemble it.
>>> import dis >>> dis.dis(Test.calc_x) 23 0 LOAD_FAST 0 (self) 3 LOAD_ATTR 0 (y) 6 LOAD_ATTR 1 (a) 9 LOAD_FAST 0 (self) 12 LOAD_ATTR 0 (y) 15 LOAD_ATTR 2 (b) 18 BINARY_ADD 19 RETURN_VALUE
We see that the function loads the variable "self" (which is always co_varnames[0]
for the associated function), then the attribute "y" ( co_names[0]
) is loaded from this object, and then the attribute 'a' ( co_names[1]
loaded from this object co_names[1]
). The second stack object is popped from self.yb, then two are added.
Look at the source dis.py in the standard library to see how the Python C binary is disassembled. Loads of the 0th variable will be important for related functions. Another convenient point is that the arguments to the co_varnames[:co_argcount]
function (the rest or varnames are locales), and co_freevars
are variables from the closing non-global area.
source share