11

In python you can do fname.__code__.co_names to retrieve a list of functions and global things that a function references. If I do fname.__code__.co_varnames, this includes inner functions, I believe.

Is there a way to essentially do inner.__code__.co_names ? by starting with a string that looks like 'inner', as is returned by co_varnames?

1
  • Is this a one level thing or you want nested functions as well? Commented Oct 28, 2014 at 13:51

2 Answers 2

7

In Python 3.4+ you can get the names using dis.get_instructions. To support nested functions as well you need to recursively loop over each code object you encounter:

import dis
import types

def get_names(f):
    ins = dis.get_instructions(f)
    for x in ins:
        try:
            if x.opcode == 100 and '<locals>' in next(ins).argval\
                                              and next(ins).opcode == 132:
                yield next(ins).argrepr
                yield from get_names(x.argval)
        except Exception:
            pass

Demo:

def func():
    x = 1
    y = 2
    print ('foo')
    class A:
        def method(self):
            pass
    def f1():
        z = 3
        print ('bar')
        def f2():
            a = 4
            def f3():
                b = [1, 2, 3]
    def f4():
        pass

print(list(get_names(func)))

Outputs:

['f1', 'f2', 'f3', 'f4']
Sign up to request clarification or add additional context in comments.

Comments

5

I don't think you can inspect the code object because inner functions are lazy, and their code-objects are only created just in time. What you probably want to look at instead is the ast module. Here's a quick example:

import ast, inspect

# this is the test scenario
def function1():
    f1_var1 = 42
    def function2():
        f2_var1 = 42
        f2_var2 = 42
        def function3():
            f3_var1 = 42

# derive source code for top-level function
src = inspect.getsource(function1)

# derive abstract syntax tree rooted at top-level function
node = ast.parse(src)

# next, ast's walk method takes all the difficulty out of tree-traversal for us
for x in ast.walk(node):
    # functions have names whereas variables have ids,
    # nested-classes may all use different terminology
    # you'll have to look at the various node-types to
    # get this part exactly right
    name_or_id = getattr(x,'name', getattr(x,'id',None))
    if name_or_id:
        print name_or_id

The results are: function1, function2, f1_var1, function3, f2_var1, f2_var2, f3_var1. Obligatory disclaimer: there's probably not a good reason for doing this type of thing.. but have fun :)

Oh and if you only want the names of the inner functions?

print dict([[x.name,x] for x in ast.walk(ast.parse(inspect.getsource(some_function))) if type(x).__name__=='FunctionDef'])

2 Comments

Good thinking. This would fail if running from a pyc, but I guess that's unusual.
Thank you, this does look like what I would be looking for. The reason I'm doing it is because I'm making an attempt at doing function traces, and I want to include inner functions in that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.