user7345804
user7345804

Reputation:

How can an outer class access data from its inner class?

I am trying to better understand how to use sub-classes using a very simple test-case based off of this question/answer.

class Outer():

    def __init__(self, x):
        self.x = super(Inner, self).__init__
        # self.x = Inner(**kwargs)

    def add_two(self):
        """ """
        return self.x + 2

    class Inner():

        def __init__(self, x=2):
            self.x = x

res = Outer(x=3).add_two()
# res = Outer({'x' : 3}).add_two()
print(res)
>> NameError: name 'Inner' is not defined

If I run the same code but make Inner() its own separate class (as opposed to a sub-class of Outer(), I receive the following error.

TypeError: super(type, obj): obj must be an instance or subtype of type

What is the cause of this error and how do I fix this?

Upvotes: 1

Views: 43

Answers (1)

jsbueno
jsbueno

Reputation: 110271

Nesting classes in Python (or other languages) seldom make sense. In this case, it is not useful for anything at all.

If on the "Outer" class you want to have an associated instance of "Inner", that should be created as an instance attribute, on the __init__ method for Outer - like this:

class Outer():

    def __init__(self, x):
        self.x = Inner(x)
        # self.x = Inner(**kwargs)

    def add_two(self):
        """ """
        return self.x + 2


class Inner():

    def __init__(self, x=2):
        self.x = x

Now, taking a step by step look on your original code, and trying to understand better why it does not work: In Python everything declared in the body of a class becomes an attribute of that class - a single copy of it will be (ordinarily) shared by all instances of that class. Declaring a whole class nested is syntactically legal but gains you nothing: the inner class is not "hidden" from the outside world by the language in any sense: not by the language, neither by the conventions usually followed by Python developers.

If you want users (i.e. other programmers, or yourself in code that makes use of this file), to create instances of "Outer" and refrain from creating instances of "Inner", simply prefix its name with an _. That is a convention in Python code, and developers usually will know that they should not trust any class, function, or other name that starts with a single _ to be safe for use in 3rd party code - that is the closest Python gets to "private" or "protected" members.

Now, getting to the line:

    ...
    self.x = super(Inner, self).__init__

It again makes no sense. super or explicitly referencing a superclass are meant to call superclasses - that is, classes from which you inherit. You created no inheritance relationship in your code, rather one of composition. That is why you get that error message - if you are using the explicit version of super, the object you pass have to be a subclass of the class you are calling super on. And it is not the case here. (Also, doing it in this form, it does not call the method - just references it - all function or method calls are comitted by the use of ())

You can also make Outer inherit from Inner - in this case, Outer will "be" an Inner, no need to keep a reference to it in an attribute - self will mean both an Outer and an Inner class. In this case, we need a reference to "Inner" when parsing the declaration of "Outer", so it needs to be defined first:

class _Inner():

    def __init__(self, x=2):
        self.x = x

class Outer(_Inner):

    def __init__(self, x):
        super().__init__(x)

    def add_two(self):
        """ """
        return self.x + 2

Note the use of parameterless super - one of the major changes for Python 3. If you need to write code still compatible with Python 2, the parameters to super can be explicit, and the call would be super(Outer, self).__init__().

(Again, calling it _Inner will mean that users of your class should not inherit or instantiate from _Inner and should use Outer - and that is a convention in coding style rather than language syntax)

Upvotes: 1

Related Questions