How to call a class method from a string representing a class name

Alex Martelli aleaxit at yahoo.com
Tue Jan 30 04:13:19 EST 2001


"Andre Fortin" <andrefor at axionet.com> wrote in message
news:3A765B29.6CDD5DD7 at axionet.com...
    [snip]
> > different specs -- "modules only" as opposed to
> > any object, "ever imported anywhere" as opposed to
> > "in the current global namespace"
>
> This is a problem. Here is the semantic I'm trying to achieve:
>
> If 'THIS module here' has heard of a module named 'test'
> call test.myFunc, which must exist according to the specs

So it's OK to raise exceptions (or also OK to mask them),
if 'test' exists AND the current module 'has heard of it',
if said module lacks a myFunc entry, or said entry's not
callable?  OK.


> if test.myFunc returns false,
> instantiate test.test and use it for further processing

OK.

> Hmmm. It looks like the sys.modules is not what I'm looking for.

It doesn't tell you whether THIS module has 'heard of' "test" --
just if "test" has ever been imported by ANY module.

> You are suggesting
>
> > callable = eval(test+'.myFunc')
>
> which is likely to be more costly compared to what I started with e.g.
>
> try:
>     myObj = globals()[className]
>     apply(vars(myObj)['myFunc'], ('Some text',))
> except KeyError:
>     pass

To make the approach you started with directly comparable to
my suggestion of using eval, let's stick to "get the callable
if any":

def getCallable1(moduleName):
    try: return eval(moduleName+'.myFunc')
    except NameError: return None

def getCallable2(moduleName):
    try:
        myObj = globals()[moduleName]
        return vars(myObj).get('myFunc')
    except KeyError: return None

Measuring these implementations shows a time per call of
(on my usual old P3/300 box) about 2 or 3 milliseconds for
getCallable1 (module present/absent) versus about 1 or 2
for getCallable2 (module present/absent).  So, yes, it IS
slower to let the very general 'eval' builtin do all the
work -- by about 1 millisecond each time.  Whether this
level of overhead IS significant, only you can tell -- it
depends, of course, on how likely it is that 'test' will
be present or absent, how much more work you must do if
it's present, and how often you will be using this in your
application -- e.g., if it's just a few thousand times and
a few seconds' total slowdown (on an old box) is tolerable
compared to your application's overal needs, then this is
no real problem.

If performance IS a crucial concern, then more work is
needed.  The key condition you want to test for is, "is
there a variable named 'test' inside the globals() for
this module"; it seems unlikely that this can be done
much faster than by getting at the globals of this module
and trying to fetch the 'test' entry!  For getting
the attribute 'myFunc' of said entry, though, getattr
appears much more direct and explicit.

def getCallable3(moduleName, globs=globals()):
    try:
        myObj = globs[moduleName]
        return getattr(myObj, 'myFunc')
    except KeyError: return None

There is not much in it, either way, between the 2 and 3
versions in terms of performance; about one microsecond's
advantage for one or the other, depending on whether
module 'test' is present or absent.  I would thus choose
to ignore any performance considerations for this choice
and go with 3 on the grounds of its relative simplicity
and directness.

Of course, you SHOULD re-do the timing on the boxes that
are of direct interest to YOUR use, IF performance on this
task is in fact crucial to your purposes (if it isn't, go
with version 1, with eval -- "do the simplest thing that
could possibly work" is my general suggestion).


Alex






More information about the Python-list mailing list