Python – nested classes in Python

classnested-classpython

Dealing with classes (nested etc) does not look easy in Python, surprisingly! The following problem appeared to me recently and took several hours (try, search …) without success. I read most of SO related links but none of them has pointed the issue presented here!

#------------------------------------
class A:
    def __init__(self):
        self.a = 'a'
        print self.a

class B(A):
    def __init__(self):
        self.b = 'b'
        A.a = 'a_b'
        print self.b, A.a
#------------------------------------
class C:
    class A:
        def __init__(self):
            self.a = 'a'
            print self.a

    class B(A):
        def __init__(self):
            self.b = 'b'
            A.a = 'a_b'
            print self.b, A.a
#------------------------------------
#------------------------------------
>>> c1 = A()
a
>>> c1.a
'a'
>>> c2 = B()
b 
>>> c2.a, c2.b
('a_b', 'b')
>>> c3 = C()
>>> c4 = c3.A()
a
>>> c4.a
'a'
>>> c5 = c3.B()
b a_b
>>> c5.b
'b'
>>> c5.a
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: B instance has no attribute 'a'

Where is the problem in the code?
AND
In both cases it seems that when B(A) is initialized A() is not initialized. What is the solution for this issue? Note that the term A.__init__() being called inside B()'s __init__() does not work!

Updates:

class Geometry:
    class Curve:
        def __init__(self,c=1):
            self.c = c                          #curvature parameter
            print 'Curvature %g'%self.c
            pass                                #some codes

    class Line(Curve):
        def __init__(self):
            Geometry.Curve.__init__(self,0)     #the key point
            pass                                #some codes

g = Geometry()
C = g.Curve(0.5)
L = g.Line()

which results in:

Curvature 0.5
Curvature 0

what I was looking for.

Best Answer

The code executed in a method runs in the local scope of that method. If you access an object that is not in this scope, Python will look it up in the global/module scope, NOT in the class scope or the scope of any enclosing class!

This means that:

A.a = 'a_b'

inside C.B.__init__ will set the class attribute of the global A class, not C.A as you probably intended. For that you would have to do this:

C.A.a = 'a_b'

Also, Python will not call parent methods if you override them in subclasses. You have to do it yourself.

The scoping rules mean that if you wanted to call the __init__ method of the parent class inside C.B.__init__, it has to look like this:

C.A.__init__(self)

and NOT like this:

A.__init__(self)

which is probably what you've tried.