[Python-Dev] Providing a mechanism for PEP 3115 compliant dynamic class creation
Daniel Urban
urban.dani+py at gmail.com
Sun Apr 15 11:36:29 CEST 2012
On Tue, Apr 19, 2011 at 16:10, Nick Coghlan <ncoghlan at gmail.com> wrote:
> In reviewing a fix for the metaclass calculation in __build_class__
> [1], I realised that PEP 3115 poses a potential problem for the common
> practice of using "type(name, bases, ns)" for dynamic class creation.
>
> Specifically, if one of the base classes has a metaclass with a
> significant __prepare__() method, then the current idiom will do the
> wrong thing (and most likely fail as a result), since "ns" will
> probably be an ordinary dictionary instead of whatever __prepare__()
> would have returned.
>
> Initially I was going to suggest making __build_class__ part of the
> language definition rather than a CPython implementation detail, but
> then I realised that various CPython specific elements in its
> signature made that a bad idea.
Are you referring to the first 'func' argument? (Which is basically
the body of the "class" statement, if I'm not mistaken).
> Instead, I'm thinking along the lines of an
> "operator.prepare(metaclass, bases)" function that does the metaclass
> calculation dance, invoking __prepare__() and returning the result if
> it exists, otherwise returning an ordinary dict. Under the hood we
> would refactor this so that operator.prepare and __build_class__ were
> using a shared implementation of the functionality at the C level - it
> may even be advisable to expose that implementation via the C API as
> PyType_PrepareNamespace().
__prepare__ also needs the name and optional keyword arguments. So it
probably should be something like "operator.prepare(name, bases,
metaclass, **kw)". But this way it would need almost the same
arguments as __build_class__(func, name, *bases, metaclass=None,
**kwds).
> The correct idiom for dynamic type creation in a PEP 3115 world would then be:
>
> from operator import prepare
> cls = type(name, bases, prepare(type, bases))
>
> Thoughts?
When creating a dynamic type, we may want to do it with a non-empty
namespace. Maybe like this (with the extra arguments mentioned above):
from operator import prepare
ns = prepare(name, bases, type, **kwargs)
ns.update(my_ns) # add the attributes we want
cls = type(name, bases, ns)
What about an "operator.build_class(name, bases, ns, **kw)" function?
It would work like this:
def build_class(name, bases, ns, **kw):
metaclass = kw.pop('metaclass', type)
pns = prepare(name, bases, metaclass, **kw)
pns.update(ns)
return metaclass(name, bases, pns)
(Where 'prepare' is the same as above).
This way we wouldn't even need to make 'prepare' public, and the new
way to create a dynamic type would be:
from operator import build_class
cls = build_class(name, bases, ns, **my_kwargs)
Daniel
More information about the Python-Dev
mailing list