在外部类作用域中找不到变量

3

我有这段代码:

class Attributes(object):
    class __metaclass__(type):
        def __init__(self, cls_name, cls_bases, cls_dict):
            # super(Attributes.__metaclass__, self) # NameError: global name 'Attributes' is not defined
            super(__metaclass__, self).__init__(
                cls_name, cls_bases, cls_dict)

提供

NameError: global name '__metaclass__' is not defined

为什么__metaclass__变量在外部作用域中找不到?


你能给我更多的代码让我测试一下吗?这样我就可以快速运行它,看看是否能够重现问题。 - Games Brainiac
我已经尝试重现这个问题,结果发现你不能在具有调用super的__init__方法的类中嵌套类。这不仅适用于第三层继承,即使你试图在Attributes内部调用super,它也不会起作用。你需要调用SimpleModel.Attributes来解决这个问题。 - Games Brainiac
3个回答

2
尝试使用这个代替。
super(Attributes.__metaclass__, self).__init__(cls_name, cls_bases, cls_dict)

谢谢你提供的解决方法。我采用了另一种方法,将相关类放在模块的顶层。 - warvariuc
1
我已经简化了问题中的示例。如果尝试使用 super(Attributes.__metaclass__, self),则会出现 NameError: global name 'Attributes' is not defined。因此,您的解决方案无法运行。 - warvariuc
Warwaruk是正确的。当元类的第一个实例被创建时,Attributes还不存在:元类先被创建,然后在外部类完成之前自动创建一个实例。 - Alfe

1
在创建类时,只有其名称可见。直到类创建完成,其内容才存在。因此,在创建过程中,类内部的部分无法访问类的任何字段。所以您需要使用完全限定的名称来表示您要访问类的字段。
您当前正在创建一个名为SimpleModel的类,并在此过程中创建一个名为Attributes的类,同时创建一个名为__metaclass__的类。由于在此过程中,类SimpleModel还不存在,因此方法__init__尚未成为任何现有内容的一部分。它首先被创建,然后稍后将成为__metaclass__类的一部分。因此,它无法知道标识符__metaclass__。由于__metaclass__也从未成为全局变量,当调用时,无法知道此标识符。
这就是为什么此时你的作用域中没有__metaclass__,但当稍后调用__init__时,全局作用域中只有一个SimpleModel,所以你可以以它为基础来命名:SimpleModel.Attributes.__metaclass__

@warwaruk:你在谈论运行时,而我在谈论编译时(如果可以真正将两者分开的话,在Python中)。当__init__被编译时,类还不存在。这就是Python不允许引用本地类而不完全限定名称的根本原因。问题:我应该在我的答案中详细说明这个方面吗?(我在第二段添加了一句(希望)澄清的话。)请注意,我谈论的是“创建一个类”,而不是“创建一个实例”... - Alfe
我在运行时遇到了引用的异常,因为在完全定义Attributes类之后,通过调用Attributes.__metaclass____new____init__来创建它。创建一个类意味着创建其元类的实例。我认为你错了。 - warvariuc
我认为你可能会被这个构造所困惑:class __metaclass__(AttributesMeta),它实际上等价于 __metaclass__ = AnotherAttributeMetaclass,其中 AnotherAttributeMetaclass 将在问题中用相同的 __metaclass__ 类体定义。请参见 https://dev59.com/WnVD5IYBdhLWcg3wE3Ro - warvariuc
我认为你的回答不适用于这个问题。它回答了另一个问题。 - warvariuc
Python在C/C++的意义上没有“编译时”。当一个模块被导入时,它实际上是运行时。 - warvariuc
显示剩余13条评论

0
看起来答案就是这样:
方法的外部作用域并不是类体,而是包含该类的外部函数或者模块。
这就是为什么在这种情况下:
class Test():
    attr_1 = 'smth'
    def a_method(self):
        attr_1  # not visible on this line

attr_1 在方法 Test.a_method 内不可见。

解决方案是在全局级别定义元类:

class AttributesMeta(type):
    def __init__(self, cls_name, cls_bases, cls_dict):
        super(__metaclass__, self).__init__(
            cls_name, cls_bases, cls_dict)

class Attributes(object):
    __metaclass__ = AttributesMeta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接