23

I have following code

fset = [ obj for name,obj in inspect.getmembers(sys.modules[__name__]) if inspect.isfunction(obj) ]

def func(num):
    pass

if __name__ == "__main__":
    print(fset)

prints

[]

however this

def func(num):
    pass

fset = [ obj for name,obj in inspect.getmembers(sys.modules[__name__]) if inspect.isfunction(obj) ]

if __name__ == "__main__":
    print(fset)

prints

[<function func at 0x7f35c29383b0>]

so how can fset be list of all functions in current module where fset is defined at the top of all functions ?

EDIT 1: What I am trying to do is

def testall(arg):
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*

def test2(arg):
    #code here
    # may call testall but wont call anyother test*

More test function may be added in the future. So thats the reason of fset/testfunctions

AirTycoon
  • 395
  • 1
  • 3
  • 10
  • why not just have fset as a function that returns a list ._. – Shashank Sep 20 '13 at 02:07
  • Why do you actually need this? And, especially, why does it need to be defined at the top? – abarnert Sep 20 '13 at 02:07
  • @abarnert It looks like I can define testfunctions at the bottom. But I thought testall would not be able to use it as it was not defined before it. – AirTycoon Sep 20 '13 at 03:13
  • @ato: `test1` has to be defined at the time it's _used_, which is when you _call_ `testall`, not when you define it. If you want to understand the technicalities, I can explain it in an answer. – abarnert Sep 20 '13 at 17:34

3 Answers3

19

EDIT 1: What I am trying to do is

def testall(arg):
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*    

This works just fine:

def testall(arg):
    testfunctions = [obj for name,obj in inspect.getmembers(sys.modules[__name__]) 
                     if (inspect.isfunction(obj) and 
                         name.startwith('test') and name != 'testall')]
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*

In this case, testfunctions isn't evaluated until testall is called, so there's no problem here—by that time, all top-level module code (including the test1 definition) will have been evaluated, so testfunctions will get all of the top-level functions. (I'm assuming here that testall or test1 is being called from an if __name__ == '__main__' block at the bottom of the module, or another script is doing import tests; tests.test1(10), or something similar.)

In fact, even if you explicitly named test1 and test2, there would be no problem:

def testall(arg):
    testfunctions = ('test1',)
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*

Again, test1 is already defined by the time you call testall, so everything is fine.


If you want to understand why this works, you have to understand the stages here.

When you import a module, or run a top-level script, the first stage is compilation (unless there's already a cached .pyc file). The compiler doesn't need to know what value a name has, just whether it's local or global (or a closure cell), and it can already tell that sys and inspect and test1 are globals (because you don't assign to them in testall or in an enclosing scope).

Next, the interpreter executes the compiled bytecode for the top-level module, in order. This includes executing the function definitions. So, testall becomes a function, then test1 becomes a function, then test2 becomes a function. (A function is really just the appropriate compiled code, with some extra stuff attached, like the global namespace it was defined in.)

Later, when you call the testall function, the interpreter executes the function. This is when the list comprehension (in the first version) or the global name lookup (in the second) happens. Since the function definitions for test1 and test2 have already been evaluated and bound to global names in the module, everything works.

What if you instead later call test1, which calls testall? No problem. The interpreter executes test1, which has a call to testall, which is obviously already defined, so the interpreter calls that, and the rest is the same as in the previous paragraph.

So, what if you put a call to testall or test1 in between the test1 and test2 definitions? In that case, test2 wouldn't have been defined yet, so it would not appear in the list (first version), or would raise a NameError (second version). But as long as you don't do that, there's no problem. And there's no good reason to do so.


If you're worried about the horrible performance cost of computing testfunctions every time you call testall… Well, first, that's a silly worry; how many times are you going to call it? Are your functions really so fast that the time to call and filter getmembers even shows up on the radar? But if it really is a worry, just cache the value in your favorite of the usual ways—mutable default, privat global, function attribute, …:

def testall(arg, _functions_cache=[]):
    if not _functions_cache:
        _functions_cache.extend([…])
abarnert
  • 354,177
  • 51
  • 601
  • 671
11

It can't be. Function definitions are executed in Python. The functions don't exist until their definition is executed. Your fset variable can't be defined until after the functions are defined.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • How about getting it from another module which sets fset by importing and inspecting the current module ? – AirTycoon Sep 20 '13 at 02:05
  • 2
    Depending on what the OP is actually trying to do, it might be sufficient to just define an `fset` _function_ at the top, then call that whenever he needs the value. Then, as long as he doesn't call the function before all of the other functions are defined, everything will be fine. (Except he might want to add `and name != 'fset'` to the end of his comprehension. It's already over 100 characters long, so what's another 15 past the edge of the screen?) – abarnert Sep 20 '13 at 02:05
  • @ato: If the other module imports the current module, (a) it will execute the code in the current module, which means a circular import, and (b) the value still won't be available until the entire current module is executed. – abarnert Sep 20 '13 at 02:07
  • @kindall: By the way, great explanation. I probably would have gone on for half a page about the compiler and other irrelevant stuff to say what you covered in two lines. :) – abarnert Sep 20 '13 at 02:14
  • @kindall: As you'll notice, to answer the OP's edit, I ended up going on for half a page about the compiler. :) Maybe you can answer the followup more clearly and concisely. – abarnert Sep 20 '13 at 19:24
  • Alternatively, open `__FILE__` and parse all `def \S+:` lines yourself. – Willem May 13 '21 at 15:36
7

To exclude any imported functions this works:

import sys
import inspect 
[obj for name,obj in inspect.getmembers(sys.modules[__name__]) 
                     if (inspect.isfunction(obj) and 
                         name.startswith('test') and
                         obj.__module__ == __name__)]
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124