'In nested classes, how to access outer class's elements from nested class in Python?

I have this scenario, where I need to ask a nested class to append items to a list in the outer class. Heres pseudocode thats similar to what Im trying to do. How would I go about getting it to work?

class Outer(object):
  outerlist = []
  class Inner(object):
    def __call__(self, arg1):
      outerlist.append(arg1)

if __name__ == "__main__":
  f = Outer()
  f.Inner("apple")
  f.Inner("orange")

  print f.outerlist()

This is what I hope to see - apple, orange

Details:
OS X, Python 2.7



Solution 1:[1]

Now that I understand your design, you're going about things all wrong.

First, Outer is a class; it doesn't get __call__ed. Your line 17 is just going to construct an empty Outer object and do nothing with it. If you want to "call" the Outer object, you can define an __init__ method—or, as Sheena suggests, define a __new__ and intercept the initialization, since you don't actually need the initialized object anyway.

Honestly, I don't think someone who doesn't understand how __call__ works yet should be trying to build something tricky like this yet. But if you insist, read on.

It's a very odd, and probably bad, design to collect this kind of stuff in a class instead of an instance. Keep in mind that class variables are effectively globals, with all that entails. If you try to use Outer reentrantly, or from multiple threads/event handlers/greenlets/whatever, the uses will end up stomping all over each other. Even if you think that isn't possibly going to be a problem now, it likely will at some point in the future.

You could create an Outer instance, and use its members as decorators. For example:

from outer_library import Outer

outer = Outer()

@outer.get("/")
…

But I'm not sure that's much better. The entire design here seems to involve performing actions at the module level, even though it looks like you're just defining normal functions and calling a function at the end. The fact that you've managed to confuse yourself should be evidence of how confusing a design this is.

But if you do want to do that, what you probably want to do is define classes inside the Outer.__init__ method, and assign them to instance members. Classes are first-class values, and can be assigned to variables just like any other values. Then, use the __init__ (or __new__) methods of those classes to do the work you wanted, making the classes simulate functions.

This may seem confusing or misleading. But remember that the whole point of what you're trying to do is to use a class in a way that it looks like a method, so that kind of confusion is inherent in the problem. But if you prefer, you can write decorators as functions (in this case, as normal instance methods of Outer); it just makes a different part of the problem harder instead of this part.

A more normal way to design something like this would be to make Outer a perfectly normal class that people can either subclass or create instances of, and provide an explicit non-fancy way to attach handler methods or functions to URLs. Then, once that's working, design a way to simplify that handler registration with a decorator.

Solution 2:[2]

This will have the desired result.

Notice the use of __new__ (line A). This means that instead of doing any construction stuff properly Inner just appends the list. Also, to access class properties of a containing class just use the outer class's name (line B).

Lastly see line C. the list is not a function. You had some extra brackets in your original code.

class Outer(object):
    outerlist = []
    class Inner(object):
        def __new__(self, arg1):                #A
            Outer.outerlist.append(arg1)        #B

f = Outer()
f.Inner("apple")
f.Inner("orange")
print f.outerlist                               #C

Solution 3:[3]

You can only access the outer class with the full global name:

class Outer(object):
    outerlist = []
    class Inner(object):
        def __call__(self, arg1):
            Outer.outerlist.append(arg1)

In python, there generally is no need to nest classes in any case. There are use-cases where it makes sense to define a class in a function (to use scoped variabels), but rarely is there a need for nested classes.

Solution 4:[4]

Why don't you pass in the outer class instance as a parameter to the inner class method? You can access the members of the outer class instance that way.

  class Outer(object):
    outerlist = []
    class Inner(object):
      def __init__(self, outer_self):
        self.outer = outer_self
      def __call__(self, arg1):
        self.outer.outerlist.append(arg1)

 if __name__ == "__main__":
   f = Outer()
   i = f.Inner(f)
   i("apple")
   i("orange")

   print f.outerlist

Solution 5:[5]

I think the option pointed out by picmate (passing the outer class as an input of the inner one) is a good choice for this particular problem. Note that it could also work without having to 'nest' the classes (in my opinion, something to avoid here since it is unnecessary). Note that I have also embedded the initialization of the inner class inside the outer one (this step is not strictly needed, but I include it here for illustrative purposes):

class Outer(object):
  outerlist = []
  def __init__(self):
    self.Inner=Inner(Outer=self)
  def get_basket(self):
    print "Outer basket: %s" %(self.outerlist)

class Inner(object):
  def __init__(self,Outer):
    self.Outer=Outer
  def __call__(self, arg1):
    self.Outer.outerlist.append(arg1)
  def get_basket(self):
    print "Inner basket: %s" %(self.Outer.outerlist)

if __name__ == "__main__":
  f = Outer()
  print "Initial state"
  f.get_basket()
  f.Inner.get_basket()
  f.Inner("apple")
  f.Inner("orange")
  print "Final state"
  f.get_basket()
  f.Inner.get_basket()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 abarnert
Solution 2
Solution 3 Martijn Pieters
Solution 4 picmate æ¶…
Solution 5 S.G.Horcas