OOP / language design question

C

cctv.star

I was wondering, why you always have to remember to call bases'
constructors explicitly from the derived class constructor? Why hasn't
this been enforced by the language?
 
R

Rene Pijlman

(e-mail address removed):
I was wondering, why you always have to remember to call bases'
constructors explicitly from the derived class constructor? Why hasn't
this been enforced by the language?

Probably because the language doesn't know whether the subclass wants to
override its base class's constructor, or enhance it.
 
D

Diez B. Roggisch

I was wondering, why you always have to remember to call bases'
constructors explicitly from the derived class constructor? Why hasn't
this been enforced by the language?

I have another question for you: why does JAVA enforce that a constructor of
a base-class must be called prior to everything else in the derived class's
constructor? No way to do some computing for parameters that I want to pass
to the parent constructor...


Besides, this automatically base-constructor-calling only happens for the
most trivial of cases - the no-argument-constructors.

Regards,

Diez
 
H

Heiko Wundram

Am Dienstag 25 April 2006 12:34 schrieb (e-mail address removed):
I was wondering, why you always have to remember to call bases'
constructors explicitly from the derived class constructor? Why hasn't
this been enforced by the language?

Because sometimes you don't want to call the base classes constructors? The
Python zen says: "Better explicit than implicit," and in this case it hits
the nail on the head. Better to see right away what your code does (the
explicit call to the base class), than to have to work around calling a bases
constructor if you don't want to call it.

--- Heiko.
 
C

cctv.star

Diez said:
I have another question for you: why does JAVA enforce that a constructor of
a base-class must be called prior to everything else in the derived class's
constructor?
Well, I can imagine it's done to make sure that the base(s) are
properly constructed. Sound s sensible to me.
No way to do some computing for parameters that I want to pass
to the parent constructor...

Try this:

Derived::Dreived() : Base(calcParam1(), calcParam2())
....
Besides, this automatically base-constructor-calling only happens for the
most trivial of cases - the no-argument-constructors.

Well, the language can at least ensure that theconstructor is called -
i.e. either call it automatically if it can be called without
parameters, or fail with error.
 
C

cctv.star

Heiko said:
Because sometimes you don't want to call the base classes constructors?
Sounds strange to me at the moment, but I'll try to adjust to this
thought.
Python zen says: "Better explicit than implicit," and in this case it hits
the nail on the head. Better to see right away what your code does (the
explicit call to the base class), than to have to work around calling a bases
constructor if you don't want to call it.
Thanks, that explains it somehow - at least, it's consistent with
explicit "self".
I think I'll need some shift in thinking after C++.
 
D

Diez B. Roggisch

Well, I can imagine it's done to make sure that the base(s) are
properly constructed. Sound s sensible to me.

It often is - there are popular examples in python where missing a
constructor will cause a program to fail spectacular. But is it _always_ a
sensible thing to do? No. If you only want some code inherited, but set up
the required constraints to do so yourself. Such things can't be expressed
in C++/JAVA, but that doesn't mean they aren't the sensible solution in
some cases.
Try this:

Derived::Dreived() : Base(calcParam1(), calcParam2())
...

Oh, I know about that. however, there are limits to this. For example, you
can't do anything that depends on constructors called before in case of
multiple inheritance. And you are forced to create a static method to do
so, which can be viewed as ugly as well.
Well, the language can at least ensure that theconstructor is called -
i.e. either call it automatically if it can be called without
parameters, or fail with error.

Yes, it can do that because of static typing - in fact it will fail on a lot
more of occasions with a compilation error.

The question here is if you are willing to trade freedom of expressiveness
against the proposed security of static typing. I found that I favor the
former and can live without the latter.

Diez
 
D

Duncan Booth

Sounds strange to me at the moment, but I'll try to adjust to this
thought.

It makes sense in more static languages such as C++. The base class is
initialised by the constructor, so you have to do everything possible to
ensure that initialisation is done before you actually try to use any part
of the base class. Weird things can still happen in C++ if you start
calling methods too soon (e.g. to calculate some of the base constructor's
parameters): you can find uninitialised member variables and you might not
get exactly the virtual methods you expected.

Python is more laid back about these things. The object itself already
exists when __init__ is first called. It already has a type, which
(unlike C++) isn't going to change part way through. All that is missing
are a few attributes, and if you try to access them too soon you'll get an
exception rather than a random value.

It makes sense therefore to give the programmer the scope to override the
expected sequence of initialisation for those rare cases where it actually
matters. The programmer also gets enough scope to shoot themselves in the
foot, but Python programmers are expected to be intelligent enough not to
do that accidentally.

Usually though, if a subclass doesn't immediately call the base class
constructors as the first thing it does in __init__ it indicates poor code
and should be refactored.

BTW, the same arguments apply to destructors: if you have a __del__ method
and need to call the base __del__ methods you have to do that manually as
well.
 
B

bruno at modulix

Duncan Booth wrote:
(snip)
Usually though, if a subclass doesn't immediately call the base class
constructors as the first thing it does in __init__ it indicates poor code
and should be refactored.

Not necessarily. It's a common case to have some computations to do/some
attributes to set in the derived class's __init__ before calling the
superclass's.
 
B

bruno at modulix

I was wondering, why you always have to remember to call bases'
constructors

<pedantic>
s/constructors/__init__/

the __init__() method is *not* the constructor. Object's instanciation
is a two-stage process: __new__() is called first, then __init__().
</pedantic>
 
C

Carl Banks

Sounds strange to me at the moment, but I'll try to adjust to this
thought.

In Java and C++, classes have private members that can only be accessed
by the class itself (and, in C++, friends). In those languages, a base
constructor needs to be called to initialize the base class's private
members.

Python has no private members (except for the double underscore
thingies, which aren't that common). Unlike C++ and Java, the derived
class's __init__ can usually initialize all the base class's members,
and it's occasionally useful to do so.

I would agree it's a mistake to not call the base class's __init__
unless you're doing it deliberately. If you want a tool to catch those
mistakes, have a look at pychecker. It can inform you whenever the
base class __init__ is not called.

(However, I totally disagree that it's a good idea to always call it
first, though. I've written base class __init__s that expected the
subclass to provide initialization methods, and some of those methods
needed some subclass members to exist before they were called.)
 
D

Duncan Booth

bruno said:
Duncan Booth wrote:
(snip)

Not necessarily. It's a common case to have some computations to
do/some attributes to set in the derived class's __init__ before
calling the superclass's.

I did only say 'usually'. Can you actually think of any good examples where
you have to set a derived attribute before you can call the base class
constructor? I can't, which is why I was a bit vague.

The base class is unlikely to depend on the derived class attributes, and
unless it does there should be no reason which you can't just call the base
__init__ straight away. Perhaps if the base __init__ calls an overridden
method, but at that point it sounds to me like something wants refactoring.

I can think that you might have to do some computations to calculate
parameters for the base __init__, but that is a separate issue.
 
C

Carl Banks

bruno said:
<pedantic>
s/constructors/__init__/

the __init__() method is *not* the constructor. Object's instanciation
is a two-stage process: __new__() is called first, then __init__().
</pedantic>

You know, Python's __init__ has almost the same semantics as C++
constructors (they both initialize something that's already been
allocated in memory, and neither can return a substitute object). I
actually think constructors are misnamed in C++, they should be called
initializers (and destructors finalizers). The only thing is that C++
doesn't always call operator new when constructing objects, whereas
Python always calls __new__, so you can put some initialization in
__new__ if you want.

Other than that I'd say that Python __init__ is analogous to Java and
C++ constructors, but is not a constructor because C++ and Java
constructors are not constructors. :) And Java has pointers, not
references. :)

A-rose-by-any-other-name-ly yr's,

Carl Banks
 
D

Duncan Booth

Carl said:
You know, Python's __init__ has almost the same semantics as C++
constructors (they both initialize something that's already been
allocated in memory, and neither can return a substitute object).

There is a significant difference: imagine B is a base type and C a
subclass of B:

When you create an object of type C in Python, while B.__init__ is
executing self is an object of type C (albeit without all the attributes
you expect on your C).

In C++ when the B() constructor is executing the object is an object of
type B. It doesn't become a C object until the C() constructor is
executing.

In other words, the object is constructed in Python before any __init__ is
called, but in C++ it isn't constructed until after all the base class
constructors have returned.
 
C

Carl Banks

Duncan said:
In other words, the object is constructed in Python before any __init__ is
called, but in C++ it isn't constructed until after all the base class
constructors have returned.

That's true. Good point.


Carl Banks
 
B

bruno at modulix

Duncan said:
bruno at modulix wrote:




I did only say 'usually'. Can you actually think of any good examples where
you have to set a derived attribute before you can call the base class
constructor?

class Base(object):
def __init__(self, arg1):
self.attr1 = arg1
self.dothis()

def dothis(self):
return self.attr1

class Derived(Base):
def __init__(self, arg1, arg2=0):
self.attr2 = arg2
Base.__init__(self, arg1)

def dothis(self):
return self.attr1 + self.attr2

(snip)
Perhaps if the base __init__ calls an overridden
method, but at that point it sounds to me like something wants refactoring.

Why so ? This is a well-known pattern (template method). I don't see
what's wrong with it.
 
D

Duncan Booth

bruno said:
class Base(object):
def __init__(self, arg1):
self.attr1 = arg1
self.dothis()

def dothis(self):
return self.attr1

class Derived(Base):
def __init__(self, arg1, arg2=0):
self.attr2 = arg2
Base.__init__(self, arg1)

def dothis(self):
return self.attr1 + self.attr2

(snip)


Why so ? This is a well-known pattern (template method). I don't see
what's wrong with it.

Apart from the fact that you can delete the method 'dothis' from both
classes with no effect on the code?

Actually, this is quite an interesting example becaue it wouldn't work in
C++: if you tried the same trick the call to dothis from the base
constructor (even assuming it is virtual) would actually call Base.dothis.

I think you have demonstrated that you can do some things in Python which
you simply cannot do in static languages like C++: assigning to derived
attributes before calling a base initialiser, and calling a virtual method
from a base initialiser. I'm not arguing about that. What I don't have
though is an example of code where doing this would actually be a good
thing.

What I think I'm trying to get at is that I believe that most situations
where someone actually tries to do something in the base initialiser which
requires calling a virtual method are probably also cases where the
initialiser is doing too much: i.e. separating the
construction/initialisation from actually doing something is usually a good
idea.
 
B

Bruno Desthuilliers

Duncan Booth a écrit :
bruno at modulix wrote:




Apart from the fact that you can delete the method 'dothis' from both
classes with no effect on the code?

Mmmm... Oh, I see. Agreed, this is not a very good example.

Is that better ?-)

Ok, let's be serious now...
Actually, this is quite an interesting example becaue it wouldn't work in
C++: if you tried the same trick the call to dothis from the base
constructor (even assuming it is virtual) would actually call Base.dothis.
I think you have demonstrated that you can do some things in Python which
you simply cannot do in static languages like C++: assigning to derived
attributes before calling a base initialiser, and calling a virtual method
from a base initialiser. I'm not arguing about that. What I don't have
though is an example of code where doing this would actually be a good
thing.

I don't have any concrete example at hand, but I can tell you I've done
such things often enough.
What I think I'm trying to get at is that I believe that most situations
where someone actually tries to do something in the base initialiser which
requires calling a virtual method

I'm afraid I fail to see what's so special about 'virtual' methods - and
FWIW, since all methods in Python are virtual, if you don't want to call
virtual methods in the initializer, you won't get very far !-)
are probably also cases where the
initialiser is doing too much: i.e. separating the
construction/initialisation from actually doing something is usually a good
idea.

What if some of the things one have to do at initialization is also used
elsewhere ? You would not duplicate code, would you ?
 
A

Alex Martelli

Duncan Booth said:
Actually, this is quite an interesting example becaue it wouldn't work in
C++: if you tried the same trick the call to dothis from the base
constructor (even assuming it is virtual) would actually call Base.dothis.
Yep.

I think you have demonstrated that you can do some things in Python which
you simply cannot do in static languages like C++: assigning to derived
attributes before calling a base initialiser, and calling a virtual method
from a base initialiser. I'm not arguing about that. What I don't have
though is an example of code where doing this would actually be a good
thing.

A recognized idiom/pattern in C++ or Java is known as "two-phase
construction" (yes, there's a two-phase destruction counterpart): have
minimal, near-empty constructors, then a separate virtual Init method
which does the actual construction work (often with a framework or at
least a factory to ensure that Init gets in fact called).

Each use case of this idiom/pattern relies on virtual methods (most
generally in a Template Method design pattern) at initialization.

E.g.: build a composite window by iteratively building subwindows
(including decorators) as enumerated by a virtual method. Initialize a
database-connection object by delegating some parts (such as connection,
local or over the net, and authentication, etc etc) to virtual methods.
And so on, and so forth.

What I think I'm trying to get at is that I believe that most situations
where someone actually tries to do something in the base initialiser which
requires calling a virtual method are probably also cases where the
initialiser is doing too much: i.e. separating the
construction/initialisation from actually doing something is usually a good
idea.

But why should that be? Template Method is perhaps the MOST generally
useful design pattern -- why would it be any less useful in
initialization than elsewhere?!


Alex
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top