convert string to a method of a class at runtime?

Kragen Sitaker kragen at pobox.com
Sun Jun 2 22:18:07 EDT 2002


"Kevin Altis" <altis at semi-retired.com> writes:
> >>> s
> 'def world(self, s):\n    print s\n\n'
> >>> c = compile(s, '', 'exec')
> >>> c
> <code object ? at 0164B620, file "", line 1>

Now 'c' is not the code object that is the body of 'world'; it is a
code object which, if executed, will create a function called 'world'.

> >>> import new
> >>> f = new.function(c, globals())
> >>> h.__class__.__dict__['world'] = new.instancemethod(f, None, h.__class__)
> >>> h.world('new world')
> Traceback (most recent call last):
>   File "<input>", line 1, in ?
> TypeError: ?() takes no arguments (2 given)

And, not surprisingly, c takes no args.

> Fingers crossed, that this kind of dynamic method addition is doable.

For Q&D, you could probably get away with exec astring in globaldict,
aclass.__dict__.

This works for me in Python 2.1.1 and 1.5.2:

class MethodBroken(Exception): pass

def methodadd(astring, aclass, globaldict=globals()):
    localdict = {}
    exec astring in globaldict, localdict
    methodnames = localdict.keys()
    if len(methodnames) != 1:
        raise MethodBroken("Method code didn't define exactly one method",
                           astring,
                           methodnames)
    name = methodnames[0]
    setattr(aclass, name, localdict[name])

I was surprised to learn not long ago that setattr() on class objects
will turn ordinary functions into instance methods for you.

> But I need to be able to do the addition outside the shell, so compiling the
> string is necessary.

exec will compile for you if you give it a string instead of a code
object.  If you want, you can compile separately:

    code = compile(astring, '', 'exec')
    exec code in globaldict, localdict

That might give you better control over error handling, for example.

> In more complex examples, the method needs the namespace to be right
> so it can make use of whatever imports and other global variables
> have been setup.

Unfortunately, I don't think there's a way to extract the globals from
a class unless it has methods.




More information about the Python-list mailing list