Inner Classes: How can I get the outer-class object at construction time?

You can use a metaclass to implement a __get__ descriptor that binds the inner class to the outer one. And since you seem to be interested in only binding to a class, consider modifying the inner class in-place, unlike a function which is wrapped into a method.

>>> class Outer(object):
    class Inner(object):
        class __metaclass__(type):
            def __get__(self, instance, owner):
                self.owner = owner
                return self


>>> Outer.Inner is Outer().Inner
True
>>> Outer.Inner.owner is Outer
True

If you'd rather wrap the inner class via a subclass then replace the __get__ body with:

return type(self.__name__, (self,), {'owner': owner})

In Python 2.6, a class decorator that's also a custom descriptor matches the specs you give:

class InnerClassDescriptor(object):
  def __init__(self, cls):
    self.cls = cls
  def __get__(self, instance, outerclass):
    class Wrapper(self.cls):
      outer = instance
    Wrapper.__name__ = self.cls.__name__
    return Wrapper

class Outer(object):
  @InnerClassDescriptor
  class Inner(object):
    def __init__(self):
      print self.outer

o = Outer()
i = o.Inner()
print 'Outer is a', type(Outer)
print 'Inner is a', type(o.Inner)

This emits:

<__main__.Outer object at 0x82f90>
Outer is a <type 'type'>
Inner is a <type 'type'>

just to confirm that

o.Inner [[is]] a class object, not something weird like a closure

as per your peculiar specs. Of course it needs to be a different class each time for reentrancy -- even in a single-threaded world, the following:

o1 = Outer()
o2 = Outer()
i1 = o1.Inner
i2 = o2.Inner
print i1(), i2(), i1(), i2()

should work cleanly, and stashing o1 vs o2 anywhere else than in the classes returned by o1.Inner vs o2.Inner (e.g., in TLS) would mean horrible results for this use.

But then you didn't specify "o.Inner has to be exactly the same class object for every possible o that's an instance of Outer", so this code fully meets the specs you did give;-).


Can't be done. But with a bit of redesign:

class Outer(object):
  pass

  class _Inner(object):
    def __init__(self, outobj):
      self.outobj = outobj

  def Inner(self):
    return self._Inner(self)

o = Outer()
i = o.Inner()

print o, i.outobj

Tags:

Python