lizs
lizs

Reputation: 550

python, how to batch create class?

I am a newbie of python, I want to create 3 class, as the following:

class ProtectTemplate(TemplateView):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectTemplate, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context['phone'] = xxx
        return context


class ProtectList(ListView):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectList, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context['phone'] = xxx
        return context


class ProtectDetail(DetailView):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectDetail, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context['phone'] = xxx
        return context

I think it is terrible. So I try to do as the following:

login_class = [
    ('ProtectTemplate', TemplateView),
    ('ProtectList', ListView),
    ('ProtectDetail', DetailView),
]

for c, v in login_class:
    class c(v):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(self.__class__, self).dispatch(*args, **kwargs)

        def get_context_data(self, **kwargs):
            context['phone'] = xxx
            return context

But it does not work. Is there anyway to batch create the 3 class?

Upvotes: 1

Views: 842

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121386

Your loop is the right approach, but you are rebinding c (not setting a new global name for the class), you can't use a variable to name the class in a class statement (the class will just be called c), and using super(self.__class__, self) will lead to infinite recursion issues.

You can use a factory function instead, and use the globals() dictionary to then add your newly created class to the global namespace. The function provides a closure so we can pass in the actual class object to super():

def _create_login_class(name, base):
    class LoginCls(base):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(LoginCls, self).dispatch(*args, **kwargs)

        def get_context_data(self, **kwargs):
            context['phone'] = xxx
            return context

    LoginCls.__name__ = name
    LoginCls.__qualname__ = name   # Python 3
    return LoginCls

_login_class = [
    ('ProtectTemplate', TemplateView),
    ('ProtectList', ListView),
    ('ProtectDetail', DetailView),
]

for _name, _base  in _login_class:
    globals()[_name] = _create_login_class(_name, _base)

The super(LoginCls, self) expression finds LoginCls as a non-local name in the parent function namespace, so always uses the correct context for finding the next class in the MRO.

I used leading-underscore names to indicate that the factory function and the _login_class, _name and _base names are implementation details for the module.

Demo:

>>> class Base:
...     def dispatch(self, *args, **kwargs): print('Base.dispatch({}, {}) called'.format(args, kwargs))
...
>>> class TemplateView(Base): pass
...
>>> class ListView(Base): pass
...
>>> class DetailView(Base): pass
...
>>> method_decorator = lambda *args: lambda f: f
>>> xxx = 'xxx'
>>> login_required = 'login_required'
>>> def _create_login_class(name, base):
...     class LoginCls(base):
...         @method_decorator(login_required)
...         def dispatch(self, *args, **kwargs):
...             return super(LoginCls, self).dispatch(*args, **kwargs)
...         def get_context_data(self, **kwargs):
...             context['phone'] = xxx
...             return context
...     LoginCls.__name__ = name
...     LoginCls.__qualname__ = name   # Python 3
...     return LoginCls
...
>>> _login_class = [
...     ('ProtectTemplate', TemplateView),
...     ('ProtectList', ListView),
...     ('ProtectDetail', DetailView),
... ]
>>> for _name, _base  in _login_class:
...     globals()[_name] = _create_login_class(_name, _base)
...
>>> ProtectTemplate
<class '__main__.ProtectTemplate'>
>>> class SubclassDemo(ProtectTemplate):
...     def dispatch(self, *args, **kwargs):
...         print('SubclassDemo.dispatch({}, {}) called'.format(args, kwargs))
...         super(SubclassDemo, self).dispatch(*args, **kwargs)
...
>>> SubclassDemo().dispatch(42, spam='ham')
SubclassDemo.dispatch((42,), {'spam': 'ham'}) called
Base.dispatch((42,), {'spam': 'ham'}) called

Upvotes: 2

Related Questions