class 语法和 type() 有什么区别?

What is the difference between class syntax and type()?

我很清楚 类 可以在 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 的正确解释。我对这两个函数进行了试验,但没有发现从这两个函数获得的 类 之间有任何显着差异。我希望从任何方面(性能方面或任何其他方面)对上述两个 patterns/syntaxes 之间的差异进行明确的解释。

谢谢,

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

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

在你的第二个例子中很难用不同的属性和方法组成一个class。

class 语句是调用 metaclass.

的声明性语法
class Foo(Bar, metaclass=SomeMeta):  # If not specified, the metaclass is type
    ...

等同于

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

其中 {...} 是从 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__

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


请注意,这意味着您可以真正滥用class声明。 EnumMeta 来自 enum 模块,在我看来,扩展了你应该用一个模块做什么。这是一个 really 辱骂的例子。 metaclass 实际上不一定是类型;它只需要是可调用的,并且需要接受三个位置参数。它也可以接受额外的关键字参数。然后它可以用这些参数做任何它想做的事情,return它也可以做任何它想做的事情。

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

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

Foo 甚至都不是 class;它绑定到 3.