为什么在创建具有元 class 的 class 的实例时出现错误

Why do I get an Error when creating an instanz of a class that has a metaclass

class  Meta(type):
    def __new__(cls, class_name, bases, attrs):

        a={}
        for name, val in attrs.items():
            if name.startswith("__"):
                a[name] = val
            else:
                a[name.upper()] = val

        return(class_name, bases, a)

class D(metaclass=Meta):
    x = 5
    y = 8

    def hello (self):
        print("hi")

c = D()
c.hello

为什么我会收到此错误:

'tuple' object is not callable line 20, in c = D()

找不到原因谁能帮帮我?

(代码基于 https://www.youtube.com/watch?v=NAQEj-c2CI8 中的视频教学)

您的元classe __new__方法中的最后一行显示为

return(class_name, bases, a)

Returns 一个简单的元组——它应该(事实上,在你评论的视频中你从中获得研究灵感的地方)调用 typetype.__new__:那里产生一个新的 class 然后可以使用:

return type(class_name, bases, a)

尽管该视频是对使用 metaclasses 进行游戏的第一个很好的介绍,(1) 这是一个高级主题,实际上一个人可以在 Python 中完成整个职业生涯,而无需需要写一个 metaclass;

(2) 在调用 type 而不是 type.__new__ 时,该视频在技术上不正确:它有效,但它具有不同的效果,因为生成的 class,虽然在其创建时被修改,将常规 type 作为其 metaclass - 通过直接调用 type,所有对 metaclass 本身的引用(Meta 在你的片段中)被丢弃。相反,如果您调用 super().__new__(cls, class_name, bases, a) ,则创建的 class 将是元 class 的一个实例(在此调用中以名称 cls 传送),并且所有子 classes 创建的 class(示例中的 D)也将具有相同的元 class 并通过此 __new__ 方法创建。

实验的一个额外细节:您正在测试以双下划线 ("__") 开头的属性 - Python 编译过程会自动破坏所有此类名称,(但名称“夹在”一对“__”之间,以包含 class 名称本身作为前缀 - 因此如果 D 具有 __x 属性,它将显示作为 _D__x 在元 class -

中收到的 attrs 字典中

我在链接的视频中看到了那里的例子;然而,这部分代码从未经过测试或验证:没有 __ 属性被证明会被保留。不过,它会将保留的“魔术”名称保留为 __init__。如前所述,该视频并不是 100% 适合教学,因为作者本身似乎只是在展示他自己的一些实验。

关于您收到的特定错误的最后一条评论:无论 metaclass __new__ 方法 returns 被 Python 作为对象绑定到 class 语句中的名称(在本例中为 D)。因此,D 变成了一个包含元组的“普通”变量,而不是新的 class。当你执行 D() 时,Python 尝试调用该对象,你得到元组不可调用的错误。