exec and closures

Peter Otten __peter__ at web.de
Thu Feb 21 17:18:16 CET 2008


Alejandro Dubrovsky wrote:

> 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?

Use a dedicated namespace:

namespace = dict(_init_=_init_)
exec template in namespace
return namespace["_autoassign"]

Peter



More information about the Python-list mailing list