25

Imagine this:

class A(object):
    class B(object):
        def __init__(self):
            super(B, self).__init__()

This creates an error:

NameError: global name B is not defined.

I've tried A.B, but then it says that A is not defined.

Update:

I've found the problem.

I've had a class like this:

class A(object):
    class B(object):
        def __init__(self):
            super(B, self).__init__()

    someattribute = B()

In that scope, A isn't defined yet.

Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • Using Python 2.6 both `A.B`, as well as `self.__class__`, seem to work. – Emil Ivanov Dec 01 '09 at 11:07
  • `A.B` works fine for me. But are you sure you need multiple inheritance and `super` here? Don't jump to the MI world unless you really need it (you usually don't). If you do go that way you need to allow and pass-through `*args` and `**kwargs` in your `__init__`. – bobince Dec 01 '09 at 11:11
  • A is not defined in its own class block, so an A class variable `_inner = B()` will not work. – u0b34a0f6ae Dec 01 '09 at 11:20
  • @Kaizer.se: Thanks, that was the problem. – Georg Schölly Dec 01 '09 at 11:34
  • A.B works for me - of cour I am not trying to instantiate B inside the A class block definition - that probably is what you are doing. The self.__class__ from Cory below should work then. – jsbueno Dec 01 '09 at 11:37

3 Answers3

20

I'm not sure why A.B is not working correctly for you, as it should.. Here's some shell output that works:

>>> class A(object):
...   class B(object):
...     def __init__(self):
...       super(A.B, self).__init__()
...   def getB(self):
...     return A.B()
... 
>>> A().getB()
<__main__.B object at 0x100496410>
Douglas Mayle
  • 21,063
  • 9
  • 42
  • 57
5

Since B will likely never be extended itself, this should work:

class A(object):
    class B(object):
        def __init__(self):
            super(self.__class__, self).__init__()
Cory Petosky
  • 12,458
  • 3
  • 39
  • 44
2

If the class A.B is unlikely to participate in any multiple inheritance, then you're better off just hard-coding the constructor call:

class A(object):
    class B(object):
        def __init__(self):
            object.__init__(self)

But if you really do need to have the full power of super, then you can get what you want by defining a custom descriptor that will initialize the B attribute lazily:

class LazyAttribute(object):
    def __init__(self, func, *args, **kwargs):
        self._func = func
        self._args = args
        self._kwargs = kwargs
        self._value = None

    def __get__(self, obj, type=None):
        if self._value is None:
            print 'created', self._value
            self._value = self._func(*self._args, **self._kwargs)
        return self._value

class A(object):
    class B(object):
        def __init__(self):
            super(A.B, self).__init__()

    someattribute = LazyAttribute(B)

This will cause the B attribute to be instantiated the first time it's accessed, and then reused thereafter:

>>> print A.someattribute
created <__main__.B object at 0x00AA8E70>
<__main__.B object at 0x00AA8E90>
>>> print A().someattribute
<__main__.B object at 0x00AA8E90>

For more info on descriptors, see: http://users.rcn.com/python/download/Descriptor.htm

Edward Loper
  • 15,374
  • 7
  • 43
  • 52