[Python-Dev] Providing a mechanism for PEP 3115 compliant dynamic class creation

Daniel Urban urban.dani+py at gmail.com
Sun Apr 15 21:34:12 CEST 2012


On Sun, Apr 15, 2012 at 13:48, Nick Coghlan <ncoghlan at gmail.com> wrote:
> /me pages thoughts from 12 months ago back into brain...

Sorry about that, I planned to do this earlier...

> On Sun, Apr 15, 2012 at 7:36 PM, Daniel Urban <urban.dani+py at gmail.com> wrote:
>> On Tue, Apr 19, 2011 at 16:10, Nick Coghlan <ncoghlan at gmail.com> wrote:
>>> 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).
>
> Yup, I believe that was my main objection to exposing __build_class__
> directly. There's no obligation for implementations to build a
> throwaway function to evaluate a class body.
>
>> __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).
>
> True.
>
>>> 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)
>
> No, I think we would want to expose the created namespace directly -
> that way people can use update(), direct assigment, exec(), eval(), or
> whatever other mechanism they choose to handle the task of populating
> the namespace. However, a potentially cleaner way to do that might be
> offer use an optional callback API rather than exposing a separate
> public prepare() function. Something like:
>
>    def build_class(name, bases=(), kwds=None, eval_body=None):
>        metaclass, ns = _prepare(name, bases, kwds)
>        if eval_body is not None:
>            eval_body(ns)
>        return metaclass(name, bases, ns)

That seems more flexible indeed. I will try to make a patch next week,
if that's OK.


Daniel


More information about the Python-Dev mailing list