Closures in metaclasses

Arnaud Delobelle arnodel at googlemail.com
Thu Jan 21 13:24:32 EST 2010


Falcolas <garrickp at gmail.com> writes:

> I'm running into an issue with closures in metaclasses - that is, if I
> create a function with a closure in a metaclass, the closure appears
> to be lost when I access the final class. I end up getting the text
> 'param' instead of the actual tags I am expecting:
>
> ALL_TAGS =  ['a', 'abbr', 'acronym', 'address', 'applet', 'b', 'bdo',
> 'big'] #snip
>
> def _tag_meta(name, bases, dict_):
>     for tag in ALL_TAGS:
>         def generic_tag(*args, **kwargs):
>             return Tag._generate_tag(tag, *args, **kwargs)
>         #generic_tag = eval("lambda *args, **kwargs: Tag._generate_tag
> ('%s', *args, **kwargs)" % tag)
>         dict_[tag] = staticmethod(generic_tag)
>     return type(name, bases, dict_)

This is almost a FAQ and has nothing to do with metaclasses.  The
simplest solution usually involves adding a default argument 'tag=tag'
to the function you define (here, generic_tag), but you can't do this
here because you have a **kwargs argument.  Instead, you can use a
closure and do this for example:

def factory(tag):
    def generic_tag(*args, **kwargs):
        return Tag._generate_tag(tag, *args, **kwargs)
    return generic_tag

def _tag_meta(name, bases, dict_):
    for tag in ALL_TAGS:
        dict_[tag] = staticmethod(factory(tag))
    return type(name, bases, dict_)

Then your Tag example class will work as you expect:

class Tag(object):
    __metaclass__ = _tag_meta
    @staticmethod
    def _generate_tag(tag_name, *args, **kwargs):
        # Does the expected, following is just for the example's sake
        return tag_name

I am sure that there are plenty of discussions of this issue in the
archives of c.l.python.  I just can't think of a good keyword to google
it ATM.

However, I am usure about why you are using a metaclass.

HTH

-- 
Arnaud



More information about the Python-list mailing list