metaclass do not call __prepare__ when use type function

Viewed 51

recently I want to convert Enum class to django's ChoicesText enum class dynamicly use code like below::

class DT(Enum):
  STRING = 'string'
  INT = 'int'

data_type = type('DataType', (django.models.enums.ChoicesText,), {tag:tag.value for tag in DT})

it raises AttributeErrorexception:

File "/usr/local/lib/python3.6/site-packages/django/db/models/enums.py", line 13, in __new__
    for key in classdict._member_names:
AttributeError: 'dict' object has no attribute '_member_names'

when debug with a toy script, I found a tricky thing is that, when use type as a function to create type, it will not call __prepare__ like class statement. as below:

# metaprepare.py
class Meta1(type):
    @classmethod
    def __prepare__(mcs, name, bases):
        print('call prepare')
        return {}

    def __new__(mcs, name, bases, parameters):
        return super().__new__(mcs, name, bases, parameters)




class A(metaclass=Meta1):
    pass


type('C', (A, ), {})

execute this script only one call prepare print.

$python metaprepare.py                                                                                                                                                       
$call prepare

it is porpose behavior or a bug?

1 Answers

Yes, it is intentional. When using type() to create a new class you are giving it everything it needs:

  • the name of the new class
  • any classes to inherit from
  • the namespace to use as the class body

That last one is important: when using the class statement Python will call __prepare__ to get the namespace to use, but when using type you are giving Python the namespace to use -- so it doesn't need to call __prepare__.

As far as your original plan, try something like:

data_type = django.models.enums.ChoicesText(
        'DataType',
        {tag:tag.value for tag in DT},
        )
Related