13

If I have a script like this:

import sys

def square(x):
    return x*x

def cube(x):
    return x**3

How can I return a list of all the functions defined locally in the program ['square', 'cube'], and not the ones imported.

They are included when I try dir() but so are all the variables and other imported modules. I don't know what to put into dir to refer to the locally executing file.

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
beroe
  • 11,784
  • 5
  • 34
  • 79

4 Answers4

19
l = []
for key, value in locals().items():
    if callable(value) and value.__module__ == __name__:
        l.append(key)
print l

So a file with the content:

from os.path import join

def square(x):
    return x*x

def cube(x):
    return x**3

l = []
for key, value in locals().items():
    if callable(value) and value.__module__ == __name__:
        l.append(key)
print l

Prints:

['square', 'cube']

Local scopes also work:

def square(x):
    return x*x

def encapsulated():
    from os.path import join

    def cube(x):
        return x**3

    l = []
    for key, value in locals().items():
        if callable(value) and value.__module__ == __name__:
            l.append(key)
    print l

encapsulated()

Prints out only:

['cube']
beroe
  • 11,784
  • 5
  • 34
  • 79
Viktor Kerkez
  • 45,070
  • 12
  • 104
  • 85
  • 8
    Even as a one_liner: `functions = [name for (name, thing) in locals().items() if callable(thing])` – 9000 Aug 26 '13 at 19:31
  • I like this solution because it doesn't rely on importing other modules, but when I try this in my actual script, it includes `'value'` as an entry in `l` for some reason that I can't figure out... "Value" doesn't appear elsewhere in the script. – beroe Aug 26 '13 at 20:41
  • @beroe are you doing it twice maybe? – Viktor Kerkez Aug 26 '13 at 20:43
  • **1.** If I have the answer of alecxe in the program along with this one, and it prints "value" from his assignment, so I guess it is working but affected by other calls to `inspect()`? **2** The one-liner by @9000 shows my imported functions too, perhaps because it omits `val.__module__ == __name__` (There is also a typo with your closing brackets). This works: `functions = [name for (name, thing) in locals().items() if (callable(thing) and thing.__module__ == __name__)]` – beroe Aug 26 '13 at 21:15
  • Oh... `key` and `value` are in `locals`, and `value` is assigned the values of all the other local variables in sequence. On Python 3, this modifies the global variable dict while iterating over it, producing undefined behavior. On Python 2, it might still produce really weird results. It's probably best to encapsulate this in a function and use `globals`. – user2357112 Aug 26 '13 at 21:24
  • Using python 3.7, list comprehensions work as expected, however when appending results to a list, I get `RuntimeError: dictionary changed size during iteration`. Presumably to get around this, `locals()` needs to be updated each iteration? Or as @user2357112supportsMonica pointed out, using `globals()` inside a function works as expected. – Josmoor98 Dec 10 '19 at 15:18
9

Use inspect module:

def is_function_local(object):
    return isinstance(object, types.FunctionType) and object.__module__ == __name__

import sys
print inspect.getmembers(sys.modules[__name__], predicate=is_function_local)

Example:

import inspect
import types
from os.path import join

def square(x):
    return x*x

def cube(x):
    return x**3

def is_local(object):
    return isinstance(object, types.FunctionType) and object.__module__ == __name__

import sys
print [name for name, value in inspect.getmembers(sys.modules[__name__], predicate=is_local)]

prints:

['cube', 'is_local', 'square']

See: no join function imported from os.path.

is_local is here, since it's a function is the current module. You can move it to another module or exclude it manually, or define a lambda instead (as @BartoszKP suggested).

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • Gives [('cube', ), ('join', ), ('square', )] when `from os.path import join` added – BartoszKP Aug 26 '13 at 19:40
  • @Marcin: That example only appears to exclude imported functions because built-in functions don't count for `inspect.isfunction`. The new version works, though. – user2357112 Aug 26 '13 at 19:49
  • 1
    I modified `is_local` to exclude itself, and this works. Thanks. `return isinstance(object, types.FunctionType) and object.__module__ == __name__ and object.__name__ !='is_local'` – beroe Aug 26 '13 at 20:45
4

Using Python 3.9.7, When trying the most upvoted answer:

l = []
for key, value in locals().items():
    if callable(value) and value.__module__ == __name__:
        l.append(key)
print(l)

I got the following error: Traceback (most recent call last): File "C:\Users\Name\Path\filename.py", line X, in for key, value in locals().items(): RuntimeError: dictionary changed size during iteration

Because the answer: locals() prints this:

'test_01': <function test_01 at 0x00000285B0F2F5E0>
'test_02': <function test_02 at 0x00000285B0F2FA60>

I just check if we get the string: "function" in the dictionary.

I used the following code to achieve my needs. Hope maybe this can help.

l = []
copy_dict = dict(locals())
for key, value in copy_dict.items():
    if "function" in str(value):
        l.append(key)
print(l)
MarcM
  • 41
  • 5
3
import sys
import inspect
from os.path import join

def square(x):
    return x*x

def cube(x):
    return x**3

print inspect.getmembers(sys.modules[__name__], \
      predicate = lambda f: inspect.isfunction(f) and f.__module__ == __name__)

Prints:

[('cube', <function cube at 0x027BAC70>), ('square', <function square at 0x0272BAB0>)]

BartoszKP
  • 34,786
  • 15
  • 102
  • 130