1

我很清楚可以在 python 中动态声明类,type并在这里和那里使用它。但是我仍然不清楚这两个功能之间有什么区别。

def class_factory():
    def init(self, attr):
        self.attr = attr
    class_ = type('DynamicClass', (), {'__init__' : init})
    return class_

def class_factory():
    class DynamicClass:
        def __init__(self, attr):
            self.attr = attr
    return DynamicClass

我已经看到在很多代码库(如 django)中使用了第一种模式,但到目前为止从未见过第二种模式。我仍然发现第二个更清楚,语法上。

我正在努力寻找关于人们为什么使用first pattern声明动态类而不是动态类的正确解释second。我对这两个函数都进行了试验,但在从这两个函数获得的类之间没有发现任何显着差异。我希望从它们不同的任何方面(性能方面或任何其他方面)清楚地解释上述两种模式/语法之间的差异。

谢谢,

4

2 回答 2

4

class语句是调用元类的声明性语法。

class Foo(Bar, metaclass=SomeMeta):  # If not specified, the metaclass is type
    ...

相当于

Foo = SomeMeta('Foo', (Bar,) {...})

where{...}是从class语句的主体构造的一些映射。仅作为一个简单的示例,无需详细说明:

class Foo(Bar, metaclass=SomeMeta):
    some_attr = "foo string"

    def __init__(self, x=9):
        self.y = x = 3

将定义一个函数名__init__,然后传递{'__init__': __init__, 'some_attr': 'foo string'}SomeMeta. 该名称__init__仅存在于class语句创建的临时命名空间中,不会影响__init__可能存在于语句之外的任何名称。

为了回答您的问题,有时自己构建元类的第三个参数比让class语句为您做更灵活或更简单。第二个论点也是如此。第一个参数由class语句的语法固定,但如果您自己调用元类,则可以在运行时生成。


请注意,这意味着您可以真正滥用该class声明。EnumMeta在我看来,从enum模块中延伸了你应该用它做什么。这是一个非常滥用的例子。元类实际上不必是类型;它必须是可调用的,并且需要接受三个位置参数。它也可以接受额外的关键字参数。然后它可以用这些参数做任何它想做的事情,并返回它想要的任何东西。

def FooMeta(name, bases, dct, msg):
    print(f'{msg} from {name}')
    return 3

class Foo(metaclass=FooMeta, msg="hi"):
    pass

Foo甚至不是一个班级;它一定会3

于 2019-11-07T17:17:13.313 回答
0

你选择了一个正确的例子,你如何用你的第二个例子动态地创建一个类?你不能正确地做到这一点,这就是为什么首选第一种情况。看看这个基于你的玩具例子。

def class_factory(many_args=False):
    def init1(self, attr):
        self.attr = attr

    def init2(self, *attr):
        self.attr = attr

    init = init1
    if many_args:
        init = init2

    class_ = type('DynamicClass', (), {'__init__': init})
    return class_

many = class_factory(True)(1, 2, 3, 4)
one = class_factory()(1)

结果 :

>>> print(many.attr, one.attr)
(1, 2, 3, 4) 1

在您的第二个示例中,很难组合具有不同属性和方法的类。

于 2019-11-07T17:10:22.250 回答