Nested classes' scope?

PythonClassScopeNestedInner Classes

Python Problem Overview


I'm trying to understand scope in nested classes in Python. Here is my example code:

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

The creation of class does not complete and I get the error:

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

Trying inner_var = Outerclass.outer_var doesn't work. I get:

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

I am trying to access the static outer_var from InnerClass.

Is there a way to do this?

Python Solutions


Solution 1 - Python

class Outer(object):
    outer_var = 1

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

This isn't quite the same as similar things work in other languages, and uses global lookup instead of scoping the access to outer_var. (If you change what object the name Outer is bound to, then this code will use that object the next time it is executed.)

If you instead want all Inner objects to have a reference to an Outer because outer_var is really an instance attribute:

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

Note that nesting classes is somewhat uncommon in Python, and doesn't automatically imply any sort of special relationship between the classes. You're better off not nesting. (You can still set a class attribute on Outer to Inner, if you want.)

Solution 2 - Python

I think you can simply do:

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

The problem you encountered is due to this:

> A block is a piece of Python program text that is executed as a unit. > The following are blocks: a module, a function body, and a class > definition.
(...)
A scope defines the visibility of a name within > a block.
(...)
The scope of names defined in a class block is > limited to the class block; it does not extend to the code blocks of > methods – this includes generator expressions since they are > implemented using a function scope. This means that the following will > fail: > > > class A:
> > a = 42
>
> b = list(a + i for i in range(10)) > > > http://docs.python.org/reference/executionmodel.html#naming-and-binding

The above means:
a function body is a code block and a method is a function, then names defined out of the function body present in a class definition do not extend to the function body.

Paraphrasing this for your case:
a class definition is a code block, then names defined out of the inner class definition present in an outer class definition do not extend to the inner class definition.

Solution 3 - Python

You might be better off if you just don't use nested classes. If you must nest, try this:

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

Or declare both classes before nesting them:

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(After this you can del InnerClass if you need to.)

Solution 4 - Python

Easiest solution:

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

It requires you to be explicit, but doesn't take much effort.

Solution 5 - Python

In Python mutable objects are passed as reference, so you can pass a reference of the outer class to the inner class.

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)

Solution 6 - Python

All explanations can be found in Python Documentation The Python Tutorial

For your first error <type 'exceptions.NameError'>: name 'outer_var' is not defined. The explanation is: >There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.

quoted from The Python Tutorial 9.4

For your second error <type 'exceptions.NameError'>: name 'OuterClass' is not defined >When a class definition is left normally (via the end), a class object is created.

quoted from The Python Tutorial 9.3.1

So when you try inner_var = Outerclass.outer_var, the Quterclass hasn't been created yet, that's why name 'OuterClass' is not defined

A more detailed but tedious explanation for your first error: >Although classes have access to enclosing functions’ scopes, though, they do not act as enclosing scopes to code nested within the class: Python searches enclosing functions for referenced names, but never any enclosing classes. That is, a class is a local scope and has access to enclosing local scopes, but it does not serve as an enclosing local scope to further nested code.

quoted from Learning.Python(5th).Mark.Lutz

Solution 7 - Python

class c_outer:
    def __init__(self, name:str='default_name'):
        self._name = name
        self._instance_lst = list()
        self._x = self.c_inner()

    def get_name(self):
        return(self._name)

    def add_inner_instance(self,name:str='default'):
        self._instance_lst.append(self.c_inner(name))

    def get_instance_name(self,index:int):
        return(self._instance_lst[index].get_name())


    class c_inner:
        def __init__(self, name:str='default_name'):
            self._name = name
        def get_name(self):
            return(self._name)


outer = c_outer("name_outer")

outer.add_inner_instance("test1")
outer.add_inner_instance("test2")
outer.add_inner_instance("test3")
inner_1 = outer.c_inner("name_inner1")
inner_2 = outer.c_inner("name_inner2")
inner_3 = outer.c_inner("name_inner3")

print(outer.get_instance_name(index=0))
print(outer.get_instance_name(1))
print(outer._instance_lst[2]._name
print(outer.get_name())
print(inner_1.get_name())
print(inner_2.get_name())

test1 test2 test3 name_outer name_inner1 name_inner2 name_inner3

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser214870View Question on Stackoverflow
Solution 1 - PythonRoger PateView Answer on Stackoverflow
Solution 2 - PythoneyquemView Answer on Stackoverflow
Solution 3 - PythonJason OrendorffView Answer on Stackoverflow
Solution 4 - PythonCristian GarciaView Answer on Stackoverflow
Solution 5 - PythonT. AlpersView Answer on Stackoverflow
Solution 6 - PythonAndy GuoView Answer on Stackoverflow
Solution 7 - PythonronView Answer on Stackoverflow