为什么在创建具有元 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 一个简单的元组——它应该(事实上,在你评论的视频中你从中获得研究灵感的地方)调用 type
或 type.__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 尝试调用该对象,你得到元组不可调用的错误。
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 一个简单的元组——它应该(事实上,在你评论的视频中你从中获得研究灵感的地方)调用 type
或 type.__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 尝试调用该对象,你得到元组不可调用的错误。