Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nested classes in Python

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.

like image 228
Developer Avatar asked Jan 08 '12 03:01

Developer


People also ask

What are nested classes in Python?

A class defined in another class is known as an inner class or nested class. If an object is created using child class means inner class then the object can also be used by parent class or root class. A parent class can have one or more inner classes but generally inner classes are avoided.

What is nested class with example?

A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules shall be obeyed. For example, program 1 compiles without any error and program 2 fails in compilation.

Does Python allow nested classes?

You can have more than one inner class in a class. As we defined earlier, it's easy to implement multiple inner classes. class Outer: """Outer Class""" def __init__(self): ## Instantiating the 'Inner' class self.


2 Answers

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.

like image 68
yak Avatar answered Oct 20 '22 07:10

yak


Nested classes seems so unpythonic, even if considered as factories. But to answer your question: There simply is no c5.a (instance of C.B). In the init-method of C.B you add to the CLASS C.A an attribute a, but not to C.B! The class A does already have an attribute a, if instantiated! But the object of class B (and even the class) doesn't!

You must also keep in mind, that __init__ is not an constructor like in C++ or Java! The "real constructor" in python would be __new__. __init__ just initializes the instance of a class!

class A:
    c = 'class-attribute'
    def __init__(self):
        self.i = 'instance-attribute'

So in this example c is a class-attribute, where i is an attribute of the instance.

Even more curios, is your attempt to add an attribute to the baseclass at the moment of the instantiation of the child-class. You are not getting a "late" inheritance-attribute that way. You simply add to the class A an additional attribute, which surprises me to even work. I guess you are using python 3.x?

The reason for this behaviour? Well, i guess it has to do with pythons neat feature that in python definitions are executed(AFAIK).

The same reason why:

def method(lst = []):

is almost ever a bad idea. the deafult-parameter gets bound at the moment of the definition and you won't generate a new list-object every-time you call the method, but reusing the same list-object.

like image 45
Don Question Avatar answered Oct 20 '22 08:10

Don Question