Reputation: 41
I'm quite new to programming and still learning my ropes. Apologies if this question is too elementary.
I feel it somewhat difficult to clarify my question, so here's an example of what I want to accomplish: Suppose that a is an instance of class X, and is supposed to have attributes a1, a2, a3, b1, b2, b3, c1, c2 and c3. I want to put a1 through a3, b1 through b3 and c1 through c3 into their own classes A, B, C nested under X for ease of use. What would be the correct syntax to do so?
class X:
def __init__ (self, name, A, B, C):
self.name = name
self.A = A
self.B = B
self.C = C
class A (X):
def _init_ (self, name, a1, a2, a3):
self.name = name
self.a1 = a1
self.a2 = a2
self.a3 = a3
class B (x):
def _init_ (self, name, b1, b2, b3):
self.name = name
self.b1 = a1
self.b2 = a2
self.b3 = a3
class C (X):
def _init_ (self, name, c1, c2, c3):
self.name = name
self.c1 = a1
self.c2 = a2
self.c3 = a3
Since This was built entirely on guesswork, I'm 99.9% certain it's wrong, but I am not sure what I should do next to make it right. I have tried searching for "nested classes" answers, but the answers I have found didn't really clarify my position.
Please explain proper Syntax for nested classes in Python.
Thanks for your help, and my apologies for this elementary question.
EDIT: There were some crucial typos in my codes that I have corrected just now. My apologies for any inconvenience.
Upvotes: 4
Views: 746
Reputation: 14952
A nested class is simply one whose definition exists within the scope of another. They generally aren't all that useful in Python. However, it is possible to do what you want and derive the attributes of one class from that of many; the two main methods are mixins / multiple inheritance and composition.
The mixin approach is like so:
class A(object):
def __init__(self, a=None, *args, **kwargs):
self.a = a
super(A, self).__init__(*args, **kwargs)
class B(object):
def __init__(self, b=None, *args, **kwargs):
self.b = b
super(B, self).__init__(*args, **kwargs)
class X(A, B):
def __init__(self, *args, **kwargs):
super(X, self).__init__(*args, **kwargs)
x = X(a='foo', b='bar')
print x.a, x.b
As X
is to have the attributes of both A
and B
, we have it inheriting from both: class X(A, B)
.
Each __init__
accepts *args, **kwargs
as parameters; our super()
call in X
passes named parameters to the initialisers of A
and B
, so we need it to be able to accept (and then ignore) parameters for the other initialisers. That is, we pass both a
and b
into A.__init__
but only use a
.
Reading up on super()
is essential for understanding Python's inheritance.
However, multiple inheritance can be difficult to manage past simple examples, so it's often recommended that you use composition instead:
class A(object):
def __init__(self, a):
self.a = a
class B(object):
def __init__(self, b):
self.b = b
class X(object):
def __init__(self, a, b):
self.A = A(a)
self.B = B(b)
x = X('foo', 'bar')
print x.A.a, x.B.b
You can see this removes the need for the super()
calls and for any special parameter handling on the initialiser, making it a lot clearer. Here we take two parameters, then construct the relevant classes internally and attach them to the X
instance. This makes attribute lookup a little more awkward, but there are ways to simplify that:
class X(object):
# <etc>
@property
def a(self):
return self.A.a
It's also possible to override __getattr__
and do some dynamic lookups instead of hand-coding each property.
class X(object):
# <etc>
def __getattr__(self, item):
for component in (getattr(self, c) for c in ['A', 'B']):
if hasattr(component, item):
return getattr(component, item)
Another option is to pass the composed instances in yourself:
class X(object):
def __init__(self, A, B):
self.A = A
self.B = B
x = X(A('foo'), B('bar'))
print x.A.a, x.B.b
This removes any need for X
to know anything about A
and B
other than their interface. This makes encapsulation easier: you can now stick A
and B
into their own modules if that's appropriate. You can also use a factory function for convenience:
def make_X(a, b):
return X(
A(a),
B(b)
)
Upvotes: 3
Reputation: 113905
You're looking for inheritance.
Also, I notice that you are missing that python is case sensitive. This means that a
and A
are NOT the same variable
class X:
def __init__ (self, name, a, b, c):
self.name = name
self.a = a
class A (X):
def _init_ (self, name, a1, a2, a3):
self.name = name
self.a1 = a1
self.a2 = a2
self.a3 = a3
class B (A):
def _init_ (self, name, a1, a2, a3):
self.name = name
self.b1 = a1
self.b2 = a2
self.b3 = a3
class C (A):
def _init_ (self, name, a1, a2, a3):
self.name = name
self.c1 = a1
self.c2 = a2
self.c3 = a3
Hope this helps
Upvotes: 1