By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,302 Members | 1,760 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,302 IT Pros & Developers. It's quick & easy.

inheritance problem with 2 cooperative methods

P: n/a
Here is a problem I am having trouble with and I hope someone in this group
will suggest a solution. First, some code that works. 3 classes that are
derived from each other (A->B->C), each one implementing only 2 methods,
__init__ and setConfig.
-------------------------------------------------------
#!/usr/bin/python
class A (object):
def __init__(self):
super(A, self).__init__()
self.x = 0
def setConfig(self, config):
self.x += config['x']

class B (A):
def __init__(self):
super(B, self).__init__()
self.y = 0
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self):
super(C, self).__init__()
self.z = 0
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A()
alpha.setConfig(config)
print alpha.x
beta = B()
beta.setConfig(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C()
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
--------------------------------------------------

The output from that code is:
1
1 2
2 4
1 2 3
2 4 6

So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects are
created and configured in one step, like this:
alpha = A(config)

How can the code be changed to implement this? It is tempting to modify the
__init__ methods like this:
class A (object):
def __init__(self, config):
super(A, self).__init__(config)
self.x = 0
A.setConfig(self, config)

However, if implemented this way, the output is:
1
2 2
3 4
3 4 3
4 6 6

This shows that setConfig gets called more than once because both __init__
and setConfig are cooperative methods.

I have been thinking about this for a day now and I cannot find a good
solution. I imagine this would be a common problem and that there would be
a recipe somewhere, but I couldn't find one (I looked in the Python
Cookbook). I've thought of creating 2 separate methods instead of
setConfig, one that does the "local" configuration and another one that
takes care of invoking super. But I don't like the idea of creating 2
methods in each class and I haven't been able to think of a way to solve the
problem with just one change in the base class that would be inherited by
all the subclasses.

Anyone has any ideas? Thanks.

Dan
Jul 18 '05 #1
Share this Question
Share on Google+
8 Replies


P: n/a
Dan Perl wrote:
So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects are
created and configured in one step, like this:
alpha = A(config)


One way would be to make the setConfig call only
in the root class, and perform the initialisation
that it depends on *before* making the super call
in each __init__ method, i.e.

class A (object):
def __init__(self, config):
self.x = 0
self.setConfig(config)

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)

This works here because each of the initialisation
operations is self-contained. It might not work so well
in real life if some of the base class state needs to be
initialised before the subclass initialisation can be
performed. However, it's worth considering -- I came
across the same sort of problem several times in
PyGUI, and I usually managed to solve it by carefully
arranging initialisations before and after the super
call.

If you can't use that solution, I would suggest you
keep the __init__ and setConfig operations separate,
and live with having to call setConfig after creating
an object. Factory functions could be provided if
you were doing this a lot.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #2

P: n/a
Dan Perl wrote:
So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects are
created and configured in one step, like this:
alpha = A(config)


One way would be to make the setConfig call only
in the root class, and perform the initialisation
that it depends on *before* making the super call
in each __init__ method, i.e.

class A (object):
def __init__(self, config):
self.x = 0
self.setConfig(config)

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)

This works here because each of the initialisation
operations is self-contained. It might not work so well
in real life if some of the base class state needs to be
initialised before the subclass initialisation can be
performed. However, it's worth considering -- I came
across the same sort of problem several times in
PyGUI, and I usually managed to solve it by carefully
arranging initialisations before and after the super
call.

If you can't use that solution, I would suggest you
keep the __init__ and setConfig operations separate,
and live with having to call setConfig after creating
an object. Factory functions could be provided if
you were doing this a lot.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #3

P: n/a
Thank you very much, Greg, that does the job! Somehow I couldn't see it and
I needed someone to point out to me.

Dan

"Greg Ewing" <gr**@cosc.canterbury.ac.nz> wrote in message
news:31*************@individual.net...
Dan Perl wrote:
So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects
are created and configured in one step, like this:
alpha = A(config)


One way would be to make the setConfig call only
in the root class, and perform the initialisation
that it depends on *before* making the super call
in each __init__ method, i.e.

class A (object):
def __init__(self, config):
self.x = 0
self.setConfig(config)

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)

This works here because each of the initialisation
operations is self-contained. It might not work so well
in real life if some of the base class state needs to be
initialised before the subclass initialisation can be
performed. However, it's worth considering -- I came
across the same sort of problem several times in
PyGUI, and I usually managed to solve it by carefully
arranging initialisations before and after the super
call.

If you can't use that solution, I would suggest you
keep the __init__ and setConfig operations separate,
and live with having to call setConfig after creating
an object. Factory functions could be provided if
you were doing this a lot.

--
Greg Ewing, Computer Science Dept,
University of Canterbury, Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #4

P: n/a
Thank you very much, Greg, that does the job! Somehow I couldn't see it and
I needed someone to point out to me.

Dan

"Greg Ewing" <gr**@cosc.canterbury.ac.nz> wrote in message
news:31*************@individual.net...
Dan Perl wrote:
So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects
are created and configured in one step, like this:
alpha = A(config)


One way would be to make the setConfig call only
in the root class, and perform the initialisation
that it depends on *before* making the super call
in each __init__ method, i.e.

class A (object):
def __init__(self, config):
self.x = 0
self.setConfig(config)

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)

This works here because each of the initialisation
operations is self-contained. It might not work so well
in real life if some of the base class state needs to be
initialised before the subclass initialisation can be
performed. However, it's worth considering -- I came
across the same sort of problem several times in
PyGUI, and I usually managed to solve it by carefully
arranging initialisations before and after the super
call.

If you can't use that solution, I would suggest you
keep the __init__ and setConfig operations separate,
and live with having to call setConfig after creating
an object. Factory functions could be provided if
you were doing this a lot.

--
Greg Ewing, Computer Science Dept,
University of Canterbury, Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #5

P: n/a
Dan Perl wrote:
Here is a problem I am having trouble with and I hope someone in this group
will suggest a solution. First, some code that works. 3 classes that are
derived from each other (A->B->C), each one implementing only 2 methods,
__init__ and setConfig.
-------------------------------------------------------
#!/usr/bin/python
class A (object):
def __init__(self):
super(A, self).__init__()
self.x = 0
def setConfig(self, config):
self.x += config['x']

class B (A):
def __init__(self):
super(B, self).__init__()
self.y = 0
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self):
super(C, self).__init__()
self.z = 0
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A()
alpha.setConfig(config)
print alpha.x
beta = B()
beta.setConfig(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C()
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
--------------------------------------------------

The output from that code is:
1
1 2
2 4
1 2 3
2 4 6

So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects are
created and configured in one step, like this:
alpha = A(config)

How can the code be changed to implement this? It is tempting to modify the
__init__ methods like this:
class A (object):
def __init__(self, config):
super(A, self).__init__(config)
self.x = 0
A.setConfig(self, config)

However, if implemented this way, the output is:
1
2 2
3 4
3 4 3
4 6 6

This shows that setConfig gets called more than once because both __init__
and setConfig are cooperative methods.

I have been thinking about this for a day now and I cannot find a good
solution. I imagine this would be a common problem and that there would be
a recipe somewhere, but I couldn't find one (I looked in the Python
Cookbook). I've thought of creating 2 separate methods instead of
setConfig, one that does the "local" configuration and another one that
takes care of invoking super. But I don't like the idea of creating 2
methods in each class and I haven't been able to think of a way to solve the
problem with just one change in the base class that would be inherited by
all the subclasses.

Anyone has any ideas? Thanks.


What about using an attribute in the base class to remember whether
initial config has been done? This seems to work:

#!/usr/bin/python
class A (object):
def __init__(self, config):
self.x = 0
self.configinitialized = False
super(A, self).__init__()
if not self.configinitialized:
self.setConfig(config)
def setConfig(self, config):
self.x += config['x']
self.configset = True

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A(config)
print alpha.x
beta = B(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
Jul 18 '05 #6

P: n/a
Dan Perl wrote:
Here is a problem I am having trouble with and I hope someone in this group
will suggest a solution. First, some code that works. 3 classes that are
derived from each other (A->B->C), each one implementing only 2 methods,
__init__ and setConfig.
-------------------------------------------------------
#!/usr/bin/python
class A (object):
def __init__(self):
super(A, self).__init__()
self.x = 0
def setConfig(self, config):
self.x += config['x']

class B (A):
def __init__(self):
super(B, self).__init__()
self.y = 0
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self):
super(C, self).__init__()
self.z = 0
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A()
alpha.setConfig(config)
print alpha.x
beta = B()
beta.setConfig(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C()
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
--------------------------------------------------

The output from that code is:
1
1 2
2 4
1 2 3
2 4 6

So far, so good! But let's assume that I want to change the __init__
methods so that they take a configuration as an argument so the objects are
created and configured in one step, like this:
alpha = A(config)

How can the code be changed to implement this? It is tempting to modify the
__init__ methods like this:
class A (object):
def __init__(self, config):
super(A, self).__init__(config)
self.x = 0
A.setConfig(self, config)

However, if implemented this way, the output is:
1
2 2
3 4
3 4 3
4 6 6

This shows that setConfig gets called more than once because both __init__
and setConfig are cooperative methods.

I have been thinking about this for a day now and I cannot find a good
solution. I imagine this would be a common problem and that there would be
a recipe somewhere, but I couldn't find one (I looked in the Python
Cookbook). I've thought of creating 2 separate methods instead of
setConfig, one that does the "local" configuration and another one that
takes care of invoking super. But I don't like the idea of creating 2
methods in each class and I haven't been able to think of a way to solve the
problem with just one change in the base class that would be inherited by
all the subclasses.

Anyone has any ideas? Thanks.


What about using an attribute in the base class to remember whether
initial config has been done? This seems to work:

#!/usr/bin/python
class A (object):
def __init__(self, config):
self.x = 0
self.configinitialized = False
super(A, self).__init__()
if not self.configinitialized:
self.setConfig(config)
def setConfig(self, config):
self.x += config['x']
self.configset = True

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A(config)
print alpha.x
beta = B(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z
Jul 18 '05 #7

P: n/a
This is almost the same code as Greg's with the only difference being that
test for configuration having been done. But the test is unnecessary. I
don't see how setConfig could be invoked in the super of the base class (A),
so such a test would be relevant only in subclasses, if they DO invoke
setConfig. But it's better if they just don't invoke it, which makes for a
much cleaner solution. Thanks anyway.

BTW, you named the attribute configinitialized in one place and configSet in
the other place. Which proves that the test is redundant, because it does
work anyway as is.

I had a similar solution, where I was invoking setConfig only if the class
of self is the same as the class where __init__ is defined, something like
this:
class A (object):
def __init__(self, config):
self.x = 0
super(A, self).__init__()
if A == self.__class__:
self.setConfig(config)

I didn't like it though because it has to be done like this in every
subclass's __init__. And it's a problem that I didn't realize at the time
if a subclass just does not need to override __init__ (then the
configuration is just not set).

Dan

"David Fraser" <da****@sjsoft.com> wrote in message
news:co**********@ctb-nnrp2.saix.net...

What about using an attribute in the base class to remember whether
initial config has been done? This seems to work:

#!/usr/bin/python
class A (object):
def __init__(self, config):
self.x = 0
self.configinitialized = False
super(A, self).__init__()
if not self.configinitialized:
self.setConfig(config)
def setConfig(self, config):
self.x += config['x']
self.configset = True

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A(config)
print alpha.x
beta = B(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z

Jul 18 '05 #8

P: n/a
This is almost the same code as Greg's with the only difference being that
test for configuration having been done. But the test is unnecessary. I
don't see how setConfig could be invoked in the super of the base class (A),
so such a test would be relevant only in subclasses, if they DO invoke
setConfig. But it's better if they just don't invoke it, which makes for a
much cleaner solution. Thanks anyway.

BTW, you named the attribute configinitialized in one place and configSet in
the other place. Which proves that the test is redundant, because it does
work anyway as is.

I had a similar solution, where I was invoking setConfig only if the class
of self is the same as the class where __init__ is defined, something like
this:
class A (object):
def __init__(self, config):
self.x = 0
super(A, self).__init__()
if A == self.__class__:
self.setConfig(config)

I didn't like it though because it has to be done like this in every
subclass's __init__. And it's a problem that I didn't realize at the time
if a subclass just does not need to override __init__ (then the
configuration is just not set).

Dan

"David Fraser" <da****@sjsoft.com> wrote in message
news:co**********@ctb-nnrp2.saix.net...

What about using an attribute in the base class to remember whether
initial config has been done? This seems to work:

#!/usr/bin/python
class A (object):
def __init__(self, config):
self.x = 0
self.configinitialized = False
super(A, self).__init__()
if not self.configinitialized:
self.setConfig(config)
def setConfig(self, config):
self.x += config['x']
self.configset = True

class B (A):
def __init__(self, config):
self.y = 0
super(B, self).__init__(config)
def setConfig(self, config):
super(B, self).setConfig(config)
self.y += config['y']

class C (B):
def __init__(self, config):
self.z = 0
super(C, self).__init__(config)
def setConfig(self, config):
super(C, self).setConfig(config)
self.z += config['z']

config = {'x':1, 'y':2, 'z':3}
alpha = A(config)
print alpha.x
beta = B(config)
print beta.x, beta.y
beta.setConfig(config)
print beta.x, beta.y
gamma = C(config)
print gamma.x, gamma.y, gamma.z
gamma.setConfig(config)
print gamma.x, gamma.y, gamma.z

Jul 18 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.