OOP / language design question

Discussion in 'Python' started by cctv.star, Apr 25, 2006.

  1. cctv.star

    cctv.star Guest

    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?
     
    cctv.star, Apr 25, 2006
    #1
    1. Advertisements

  2. cctv.star

    Rene Pijlman Guest

    :
    Probably because the language doesn't know whether the subclass wants to
    override its base class's constructor, or enhance it.
     
    Rene Pijlman, Apr 25, 2006
    #2
    1. Advertisements

  3. 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
     
    Diez B. Roggisch, Apr 25, 2006
    #3
  4. Am Dienstag 25 April 2006 12:34 schrieb :
    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.
     
    Heiko Wundram, Apr 25, 2006
    #4
  5. cctv.star

    cctv.star Guest

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

    Derived::Dreived() : Base(calcParam1(), calcParam2())
    ....
    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.
     
    cctv.star, Apr 25, 2006
    #5
  6. cctv.star

    cctv.star Guest

    Sounds strange to me at the moment, but I'll try to adjust to this
    thought.
    Thanks, that explains it somehow - at least, it's consistent with
    explicit "self".
    I think I'll need some shift in thinking after C++.
     
    cctv.star, Apr 25, 2006
    #6
  7. cctv.star

    Rene Pijlman Guest

    :
    +1 qotw
     
    Rene Pijlman, Apr 25, 2006
    #7
  8. Well, I can imagine it's done to make sure that the base(s) are
    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.
    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.
    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
     
    Diez B. Roggisch, Apr 25, 2006
    #8
  9. cctv.star

    Duncan Booth Guest

    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.
     
    Duncan Booth, Apr 25, 2006
    #9
  10. 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.
     
    bruno at modulix, Apr 25, 2006
    #10
  11. <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>
     
    bruno at modulix, Apr 25, 2006
    #11
  12. cctv.star

    Carl Banks Guest

    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.)
     
    Carl Banks, Apr 25, 2006
    #12
  13. cctv.star

    Duncan Booth Guest

    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.
     
    Duncan Booth, Apr 25, 2006
    #13
  14. cctv.star

    Carl Banks Guest

    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
     
    Carl Banks, Apr 25, 2006
    #14
  15. cctv.star

    Duncan Booth Guest

    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.
     
    Duncan Booth, Apr 25, 2006
    #15
  16. cctv.star

    Carl Banks Guest

    That's true. Good point.


    Carl Banks
     
    Carl Banks, Apr 25, 2006
    #16
  17. 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.
     
    bruno at modulix, Apr 25, 2006
    #17
  18. cctv.star

    Duncan Booth Guest

    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.
     
    Duncan Booth, Apr 25, 2006
    #18
  19. Duncan Booth a écrit :
    Mmmm... Oh, I see. Agreed, this is not a very good example.

    Is that better ?-)

    Ok, let's be serious now...
    I don't have any concrete example at hand, but I can tell you I've done
    such things often enough.
    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 !-)
    What if some of the things one have to do at initialization is also used
    elsewhere ? You would not duplicate code, would you ?
     
    Bruno Desthuilliers, Apr 26, 2006
    #19
  20. 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.

    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
     
    Alex Martelli, Apr 26, 2006
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.