嵌套类的范围?


116

我试图了解Python嵌套类中的作用域。这是我的示例代码:

class OuterClass:
    outer_var = 1
    class InnerClass:
        inner_var = outer_var

类的创建未完成,并且出现错误:

<type 'exceptions.NameError'>: name 'outer_var' is not defined

尝试inner_var = Outerclass.outer_var不起作用。我得到:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined

我正在尝试从访问静态outer_var信息InnerClass

有没有办法做到这一点?

Answers:


105
class Outer(object):
    outer_var = 1

    class Inner(object):
        @property
        def inner_var(self):
            return Outer.outer_var

这与其他语言中的类似功能不太一样,并且使用全局查找而不是限制对的访问outer_var。(如果更改名称Outer绑定到的对象,则此代码将在下次执行该对象时使用该对象。)

相反,如果您希望所有Inner对象都具有对的引用,Outer因为outer_var它实际上是实例属性:

class Outer(object):
    def __init__(self):
        self.outer_var = 1

    def get_inner(self):
        return self.Inner(self)
        # "self.Inner" is because Inner is a class attribute of this class
        # "Outer.Inner" would also work, or move Inner to global scope
        # and then just use "Inner"

    class Inner(object):
        def __init__(self, outer):
            self.outer = outer

        @property
        def inner_var(self):
            return self.outer.outer_var

请注意,嵌套类在Python中并不常见,并且不会自动暗示类之间的任何特殊关系。您最好不要嵌套。(您仍然可以设置一个类属性上OuterInner,如果你想要的。)


2
添加您的答案将使用哪个版本的python可能会有所帮助。
AJ。

1
我在编写此文件时的初衷是2.6 / 2.x,但是从它的角度来看,我发现在3.x中不会有任何不同之处。

我在这部分的意思不是很清楚,“((如果更改名称Outer绑定到的对象,那么该代码将在下一次执行时使用该对象。)”您能否帮助我理解?
batbrat 2014年

1
@batbrat表示Outer每次执行时都重新查找对的引用Inner.inner_var。因此,如果将名称重新绑定Outer到新对象,Inner.inner_var将开始返回该新对象。
费利佩2014年

45

我认为您可以做到:

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

您遇到的问题是由于以下原因:

块是作为单元执行的一段Python程序文本。以下是块:模块,函数体和类定义。
(...)
范围定义了块中名称的可见性。
(...)
在类块中定义的名称范围仅限于该类块;它不会扩展到方法的代码块–包括生成器表达式,因为它们是使用函数范围实现的。这意味着以下操作将失败:

   class A:  

       a = 42  

       b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

上面的意思是:
一个函数体是一个代码块,一个方法是一个函数,那么在类定义中存在于该函数体之外的名称将不会扩展到该函数体。

用您的情况解释一下:
类定义是一个代码块,然后在外部类定义中存在的内部类定义之外定义的名称不会扩展到内部类定义。


2
惊人。您的示例失败,声称“未定义全局名称'a'”。然而,替换列表理解[a + i for i in range(10)]成功将Ab绑定到预期列表[42..51]。
乔治

6
@George请注意,类A的示例不是我的,它来自我提供链接的Python官方文档。此示例失败,而该失败正是本示例所要显示的。其实list(a + i for i in range(10))就是list((a + i for i in range(10)))这样说list(a_generator)。他们说,生成器的实现范围与功能范围类似。
eyquem

@George对我来说,这意味着函数在模块或类中的行为不同。在第一种情况下,函数会走出去查找绑定到空闲标识符的对象。在第二种情况下,函数(即方法)不会超出其主体范围。实际上,模块中的函数和类中的方法是两种对象。方法不仅仅是类中的函数。那是我的主意。
eyquem

5
@George:FWIW,list(...)调用和理解都无法在Python 3中工作。Py3 的文档也略有不同,反映了这一点。现在它说:“ 在类块中定义的名称范围仅限于该类块;它不扩展至方法的代码块–这包括理解和生成器表达式,因为它们是使用函数范围实现的。 ”(强调我的)。
martineau 2013年

我很好奇为什么list(Aa + i for range(10)中的i)也不起作用,我在哪里用Aa固定了a,我认为A可能是一个全局名称。
gaoxinge'2

20

如果您不使用嵌套类,则可能会更好。如果必须嵌套,请尝试以下操作:

x = 1
class OuterClass:
    outer_var = x
    class InnerClass:
        inner_var = x

或在嵌套它们之前声明两个类:

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(在此之后,您可以del InnerClass根据需要。)


3

最简单的解决方案:

class OuterClass:
    outer_var = 1
    class InnerClass:
        def __init__(self):
            self.inner_var = OuterClass.outer_var

它要求您保持明确,但不需要花费很多精力。


NameError:名称'
OuterClass

3
关键是要从Outer类范围访问它-如果从Outer内部的静态范围调用它,也会发生类似的错误。我建议您删除此信息
Mr_and_Mrs_D 2015年

1

在Python中,可变对象作为引用传递,因此您可以将外部类的引用传递给内部类。

class OuterClass:
    def __init__(self):
        self.outer_var = 1
        self.inner_class = OuterClass.InnerClass(self)
        print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)

    class InnerClass:
        def __init__(self, outer_class):
            self.outer_class = outer_class
            self.inner_var = 2
            print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)

2
请注意,这里有一个参考循环,在某些情况下此类的实例不会被释放。一个使用cPython的示例,如果您已定义__del__ 方法,则垃圾收集器将无法处理引用周期,并且对象将进入gc.garbage。上面的代码原样没有问题。处理它的方法是使用弱引用。您可以阅读有关weakref(2.7)weakref(3.5)的
Guyarad

0

所有说明都可以在Python文档中找到。

对于您的第一个错误<type 'exceptions.NameError'>: name 'outer_var' is not defined。解释是:

没有从方法内部引用数据属性(或其他方法!)的捷径。我发现这实际上提高了方法的可读性:浏览方法时,不会混淆局部变量和实例变量。

引自《 Python教程9.4》

对于第二个错误 <type 'exceptions.NameError'>: name 'OuterClass' is not defined

当正常保留类定义时(通过结尾),将创建一个类对象。

引自Python教程9.3.1

因此,当您尝试时inner_var = Outerclass.outer_varQuterclass尚未创建,这就是为什么name 'OuterClass' is not defined

有关第一个错误的更详细但乏味的解释:

尽管类可以访问封闭函数的作用域,但是它们不能充当嵌套在类内的代码的封闭作用域:Python在封闭函数中搜索引用的名称,但从不搜索任何封闭类。也就是说,一个类是一个局部作用域,可以访问封闭的局部作用域,但不能用作进一步嵌套代码的封闭的局部作用域。

引用自Learning.Python(5th).Mark.Lutz

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.