在嵌套类实例中,如何/为什么全局变量可用/在范围内,而“非本地”变量却不可用?

时间:2019-03-09 05:29:53

标签: python python-3.x class inheritance nested

如何以类似于访问全局变量的方式访问OuterClass变量?

例如:

global_variable = 'global_variable'

class InnerClass:

    def __init__(self):
        pass

    def test(self):
        return super().variable

    def test_global(self):
        return global_variable


class OuterClass:

    def __init__(self, inner_class_instance):
        self.variable = 'class_variable'
        self.inner_class_instance = inner_class_instance

运行以下命令将返回全局变量:

inner = InnerClass()
outer = OuterClass(inner)
outer.inner_class_instance.test_global()

但是,尝试访问nonlocal类变量会导致AttributeError

outer.inner_class_instance.test()

super()InnerClass.test中使用不正确,因为OuterClass不是InnerClass的基类。如何以与访问全局变量类似的方式访问OuterClass变量?但这没有将上下文参数传递给InnerClass。在nonlocal variable中使用InnerClass.test会产生SyntaxError

此外,如何以及为什么从InnerClass.test访问全局变量?

3 个答案:

答案 0 :(得分:1)

test(self)的{​​{1}}方法中,您错误地理解了表达式InnerClass的含义。 super().variable在这里是指super()的超类,尽管它在代码中没有明确说明,但恰好是称为InnerClass的预定义类。并且Object类当然没有名为Object的属性。这就是为什么该行引发错误

  

“超级”对象没有属性“变量”

您的另一个问题是“如何访问variable变量?”。

我想先在这里整理一下概念的一些基本方面。

首先,您在OuterClass中拥有的名称为InnerClass的东西实际上是variable属性。因此,我宁愿将您的问题重新表述为“如何通过InnerClass的方法访问OuterClass 属性?”。

第二,仅仅因为InnerClass的{​​{1}}方法执行时OuterClass收到了对InnerClass实例的引用,并不意味着__init__()绝对是“外部”或“周围” OuterClass

OuterClass中的代码的角度来看,InnerClass只是另一个类,没有特殊的地位-它既不是超类(祖先类),也不是外部类(周围的阶级)。因此,要从称为InnerClass的{​​{1}}方法内部访问OuterClass实例的属性,首先需要一个名称,该名称包含对OuterClass实例的引用。因此,例如,在InnerClass方法中,如果您碰巧有一个名为test()的名称,该名称包含对OuterClass实例的引用,则可以肯定地引用{{1 }}属性称为test(),使用my_outer_inst

答案 1 :(得分:0)

从第一个问题开始,在OuterClass的代码中还有更多要定义的内容。 OuterClass variable ,这表示它不是在类内部定义的。

尝试执行此操作,my_outer_inst.variable。您再次得到相同的variable。定义/声明AttributeError,然后尝试一次。

第二个问题,''super' object has no attribute 'variable''变量是在类外部声明的变量。可以在任何地方访问它们。但是,为了更改其值,必须在类内部将其声明为inner.test()

答案 2 :(得分:0)

被分配为其他实例的属性的实例通常无法获得对包含实例的引用。一方面,此类引用可能为零或不止一个!例如,如果您使用相同的OuterClass实例创建了InnerClass的第二个实例,则可能要读取两个可能不同的variable属性。

通常,如果要访问对象的属性,则需要首先引用该对象。除非您安排一些替代的API,否则仅对您的引用是不够的。

例如,也许InnerClass希望在其OuterClass方法中获得对test的引用,而OuterClass得到一种方法来传递自身:

class InnerClass:
    def test(self, outer):
        return outer.variable

class OuterClass:
    def __init__(self, inner, variable):
        self.inner = inner
        self.variable = variable

    def run_test(self):
        return self.inner.test(self)

out = OuterClass(InnerClass(), "foo")
print(out.run_test())

您还可以让您的类设置循环引用,它们之间互相引用。然后,任何一个都可以与另一个做些事情:

class InnerClass:
    def __init__(self):
        self.outer = None

    def test(self):
        if self.outer is None:
            raise ValueError("Not in an OuterClass instance!")
        return self.outer.variable

 class OuterClass:
    def __init__(self, inner, variable):
        self.inner = inner
        inner.outer = self      # set reference back to us!
        self.variable = variable

out = OuterClass(InnerClass(), "foo")
print(out.inner.test())

这是这种方法的非常粗糙的版本,您可能想要确保引用保持一致(例如,防止两个不同的InnerClass实例使用同一OuterClass实例)。

请注意,像这样的循环引用使垃圾收集器的工作更加困难。通常,Python对象的最后一个引用消失后会立即清除它们。但是具有引用循环的对象之间始终总是存在引用之间的引用,因此GC需要检查整个对象集是否全部都消失了。对于一个仅包含两个对象的循环,如本例所示,它可能会很好地管理它,但是在更大的数据结构中,它可能会更棘手。