exec and closures

Alejandro Dubrovsky alito at organicrobot.com
Thu Feb 21 09:15:38 EST 2008


About a month ago, there was a thread on auto-assigning decorators for 
__init__.  One by André Roberge is here:
http://groups.google.com/group/comp.lang.python/browse_frm/
thread/32b421bbe6caaeed/0bcd17b1fa4fb07c?#0bcd17b1fa4fb07c

This works well for simple cases, but doesn't take keyword arguments or 
set default values.  I wrote a more extensive version implementing python 
call semantics, but it seemed awkard to be repeating something the 
compiler does already, so I tried execing a function definition on the 
fly with the right parameters that would function as the decorator.  Like 
this  (adjust the indentation variable if it throws a syntax error)

def autoassign(_init_):
        import inspect
        import functools

        argnames, _, _, defaults = inspect.getargspec(_init_)
        argnames = argnames[1:]

        indentation = '        '
        settings = ['self.%s = %s' % (arg[1:], arg) for arg in argnames 
if arg[0] == '_']

        if len(settings) <= 0:
                return _init_
        
        if defaults is None:
                args = argnames[:]
        else:
                args = argnames[:-len(defaults)]
                for key, value in zip(argnames[-len(defaults):],defaults):
                        args.append('%s=%s' % (key, repr(value)))

        template = """def _autoassign(self, %(args)s):
%(setting)s
        _init_(self, %(argnames)s)
""" % {'args' : ", ".join(args), 'setting' : "\n".join(['%s%s' % 
(indentation, setting) for setting
in settings]), 'argnames' : ', '.join(argnames)}
                
        try:
                exec template
        except SyntaxError, e:
                raise SyntaxError('%s. line: %s. offset %s:\n%s' % 
(e.msg, e.lineno, e.offset, template))
        return _autoassign


Which creates what looked like the right template, but when instantiating 
a class that uses that (eg
class A(object):
        @autoassign
        def __init__(self,_a):
                pass
a = A(3)

it throws a
NameError: global name '_init_' is not defined

Is there a way to bind the _init_ name at exec time?

Thanks,
ale



More information about the Python-list mailing list