python,如何批量创建类?

python, how to batch create class?

我是Python的新手,我想创建3个类,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

我觉得很糟糕。所以我试着这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

但它不起作用。是否仍要批量创建3类?


您的循环是正确的方法,但是您正在重新绑定c(而不是为类设置新的全局名称),您不能在class语句中使用变量来命名类(该类将被称为c,使用super(self.__class__, self)将导致无限的递归问题。

您可以使用工厂函数,然后使用globals()字典将新创建的类添加到全局命名空间。函数提供了一个闭包,因此我们可以将实际类对象传递给super()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)

super(LoginCls, self)表达式在父函数命名空间中查找LoginCls作为非本地名称,因此始终使用正确的上下文来查找mro中的下一个类。

我使用了前导下划线名称来表示工厂函数和_login_class_name_base名称是模块的实现细节。

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
>>> 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