[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