Multiple constructors

Discussion in 'Python' started by Philip Smith, Feb 6, 2005.

  1. Philip Smith

    Philip Smith Guest

    Call this a C++ programmers hang-up if you like.

    I don't seem to be able to define multiple versions of __init__ in my matrix
    class (ie to initialise either from a list of values or from 2 dimensions
    (rows/columns)).

    Even if Python couldn't resolve the __init__ to use on the basis of argument
    types surely it could do so on the basis of argument numbers???

    At any rate - any suggestions how I code this????

    Thanks

    Phil
     
    Philip Smith, Feb 6, 2005
    #1
    1. Advertising

  2. Philip Smith wrote:
    > I don't seem to be able to define multiple versions of __init__ in my matrix
    > class (ie to initialise either from a list of values or from 2 dimensions
    > (rows/columns)).


    You could either use an if statement with *args:

    class Matrix(object):
    def __init__(self, *args):
    if len(args) == 1:
    # Initialize from list of values
    elif len(args) == 2:
    # Initialize from rows/columns
    else:
    raise TypeError("Constructor accepts 1 or 2 arguments.")

    Or with two different functions:

    class Matrix(object):
    def __init__(self, values):
    # Initialize from a list of values

    @classmethod
    def from_pair(self, rows, columns):
    return Matrix([rows, columns]) # Or with the right argument
     
    Leif K-Brooks, Feb 6, 2005
    #2
    1. Advertising

  3. Philip Smith wrote:
    > Call this a C++ programmers hang-up if you like.
    >
    > I don't seem to be able to define multiple versions of __init__ in my matrix
    > class (ie to initialise either from a list of values or from 2 dimensions
    > (rows/columns)).
    >
    > Even if Python couldn't resolve the __init__ to use on the basis of argument
    > types surely it could do so on the basis of argument numbers???
    >
    > At any rate - any suggestions how I code this????


    Checking the number of arguments ain't all that hard:

    class Klass:
    def __init__(*args):
    self.args = args
    if len(self.args) == 1:
    # etc.

    This feels rather unpythonic, though. Maybe you could use factory
    functions, forgetting about __init__ all together (2.2 or higher):

    class Klass(object):

    def fromList(seq):
    result = Klass()
    # populate attributes here
    # and return the requested object
    return result

    fromList = staticmethod(fromList)

    def fromDimensions(cols, rows):
    result = Klass()
    # populate attributes here
    # and return the requested object
    return result

    fromDimensions = staticmethod(fromDimensions)

    #more methods here


    k = Klass.fromList(seq)
    etc..


    Regards
    --

    Vincent Wehren







    >
    > Thanks
    >
    > Phil
    >
    >
     
    vincent wehren, Feb 6, 2005
    #3
  4. Leif K-Brooks wrote:
    > @classmethod
    > def from_pair(self, rows, columns):
    > return Matrix([rows, columns]) # Or with the right argument


    Er... I'm not sure why I named that argument "self", it should be "cls"
    if you don't want to confuse anyone reading your code.
     
    Leif K-Brooks, Feb 6, 2005
    #4
  5. Philip Smith

    Terry Reedy Guest

    "Philip Smith" <> wrote in message
    news:6SjNd.38337$...
    > Call this a C++ programmers hang-up if you like.
    >
    > I don't seem to be able to define multiple versions of __init__ in my
    > matrix


    Correct.

    > class (ie to initialise either from a list of values or from 2 dimensions
    > (rows/columns)).
    >
    > Even if Python couldn't resolve the __init__ to use on the basis of
    > argument types surely it could do so on the basis of argument numbers???


    Variable parameter counts are handled either with default values or the
    *restlist and **keydict mechanisms.

    Keep in mind that the compiler cannot, in general, know, at compile time,
    what function object will be bound to a name at run time. And that you can
    have only bind a name to one object.

    > At any rate - any suggestions how I code this????


    The usual way is to write your own dispatch code to either execute the
    appropriate code block or call the appropriate function.

    Or you could write a function of functions that returns a function that
    dispatches to one of the functions according to its arg count. Something
    like (untested, all exceptions passed through):

    def arg_count_dispatcher_maker(*funcs):
    def arg_count_dispatcher(*args):
    return funcs[len(args)](*args)
    return arg_count_dispatcher

    which you use like this:
    __init__ = arg_count_dispatcher_maker(func0, func1, func2)

    Terry J. Reedy
     
    Terry Reedy, Feb 6, 2005
    #5
  6. Philip Smith <> wrote:

    > Call this a C++ programmers hang-up if you like.
    >
    > I don't seem to be able to define multiple versions of __init__ in my matrix


    Indeed, you can never define ``multiple versions'' of the same name in
    the same scope: one scope + one name -> one object.

    That's what a name (in a given scope, which I won't keep repeating)
    MEANS -- in Python as well as in common sense.

    > class (ie to initialise either from a list of values or from 2 dimensions
    > (rows/columns)).
    >
    > Even if Python couldn't resolve the __init__ to use on the basis of argument
    > types surely it could do so on the basis of argument numbers???


    It could, if it didn't think a name is a name is a name. By sticking to
    resolution JUST BY NAME, instead of by name plus who knows what else,
    however, Python gains a lot of conceptual simplicity without any loss of
    functionality. Therefore, it's a great design choice.


    > At any rate - any suggestions how I code this????


    My preferred suggestion is to accept that one name === one object: you
    want two different objects (constructors), give them two different
    names. One, if you wish, can be __init__ -- the other could be a
    staticmethod or even better a classmethod. Or, have two named methods.

    class Matrix(object):
    def __init__(self, values):
    " init self from values "
    @classmethod
    def withDimensions(cls, x, y):
    return cls([0.0]*x for i in xrange(y))
    @classmethod
    def fromValues(cls, values):
    return cls(values)

    Now, Matrix.withDimensions(3, 4) and Matrix.fromValues([[1,2],[3,4]])
    are both available and maximally clear, and the latter you can also call
    as Matrix([[1,2],[3,4]]) if you wish. The advantage of using
    classmethod is that if you later go and subclass

    class SpecialMatrix(Matrix):
    ...

    you can call the classmethods on this subclass and get an instance of
    the subclass, which can sometimes be handy -- better than using
    staticmethods (or factory functions ``outside the class'') and
    ``hardwiring'' what class they instantiate.


    I don't particularly like the concept of a function or method which does
    drastically different things -- I'd rather see one function have ONE
    function (taking the second repetition as meaning ``role'', ``task'').
    This goes for __init__, too. Still, if you're keen on the idea, you can
    of course have your __init__ take optional arguments, check their
    presence and/or type, and whatever other devilry; I just think it's not
    a very good design, but it does end up with just the same effect as C++
    overloaded constructors, which you seem to like. If you want to do this
    all the time, you could even build appropriate infrastructure for this
    task -- a little custom descriptor and metaclass, and/or decorators.
    Such infrastructure building is in fact fun and instructive -- as long
    as you don't fall into the trap of *using* such complications in
    production code, where Python's simplicity rules;-).


    Alex
     
    Alex Martelli, Feb 6, 2005
    #6
  7. vincent wehren wrote:
    > Philip Smith wrote:
    >> Call this a C++ programmers hang-up if you like.
    >>
    >> I don't seem to be able to define multiple versions of __init__ in my matrix
    >> class (ie to initialise either from a list of values or from 2 dimensions
    >> (rows/columns)).
    >>
    >> Even if Python couldn't resolve the __init__ to use on the basis of argument
    >> types surely it could do so on the basis of argument numbers???
    >>
    >> At any rate - any suggestions how I code this????

    >
    > Checking the number of arguments ain't all that hard:
    >
    > class Klass:
    > def __init__(*args):
    > self.args = args
    > if len(self.args) == 1:
    > # etc.
    >
    > This feels rather unpythonic, though.


    And it won't work, as `self' is not defined. ;)

    Reinhold
     
    Reinhold Birkenfeld, Feb 6, 2005
    #7
  8. Philip Smith

    Philip Smith Guest

    Thanks to all of you

    Some useful ideas in there, even if some of them stretch my current
    knowledge of the language.

    C++ to Python is a steep 'unlearning' curve...

    Phil

    "Philip Smith" <> wrote in message
    news:6SjNd.38337$...
    > Call this a C++ programmers hang-up if you like.
    >
    > I don't seem to be able to define multiple versions of __init__ in my
    > matrix class (ie to initialise either from a list of values or from 2
    > dimensions (rows/columns)).
    >
    > Even if Python couldn't resolve the __init__ to use on the basis of
    > argument types surely it could do so on the basis of argument numbers???
    >
    > At any rate - any suggestions how I code this????
    >
    > Thanks
    >
    > Phil
    >
     
    Philip Smith, Feb 6, 2005
    #8
  9. Reinhold Birkenfeld wrote:
    > vincent wehren wrote:
    >
    >>Philip Smith wrote:
    >>
    >>>Call this a C++ programmers hang-up if you like.
    >>>
    >>>I don't seem to be able to define multiple versions of __init__ in my matrix
    >>>class (ie to initialise either from a list of values or from 2 dimensions
    >>>(rows/columns)).
    >>>
    >>>Even if Python couldn't resolve the __init__ to use on the basis of argument
    >>>types surely it could do so on the basis of argument numbers???
    >>>
    >>>At any rate - any suggestions how I code this????

    >>
    >>Checking the number of arguments ain't all that hard:
    >>
    >>class Klass:
    >> def __init__(*args):
    >> self.args = args
    >> if len(self.args) == 1:
    >> # etc.
    >>
    >>This feels rather unpythonic, though.

    >
    >
    > And it won't work, as `self' is not defined. ;)


    You're right of course!

    Note to self: Must stop shooting from the hip ;)


    --
    Vincent




    >
    > Reinhold
     
    vincent wehren, Feb 6, 2005
    #9
  10. Alex Martelli wrote:
    > If you want to do this
    > all the time, you could even build appropriate infrastructure for this
    > task -- a little custom descriptor and metaclass, and/or decorators.
    > Such infrastructure building is in fact fun and instructive -- as long
    > as you don't fall into the trap of *using* such complications in
    > production code, where Python's simplicity rules;-).


    +1 QOTW.

    I think this is one of the "great truths" of Python. Descriptors,
    metaclasses, decorators, etc. are all there to let you do interesting,
    useful things. But if you're using such constructs for more than a few
    special cases, then you're missing out on a simple solution that Python,
    almost certainly, makes beautiful.

    Steve
     
    Steven Bethard, Feb 6, 2005
    #10
  11. Hi Philip

    > C++ to Python is a steep 'unlearning' curve...


    That's worthy of QOTW. I decided not to reply to this thread earlier, but
    you just convinced me otherwise :)

    I work in Delphi a lot, which is in a lot of respects very similar to C.
    I have come to the conclusion that function overloading was introduced to
    allow the same calling syntax and even functionality to be applied to
    different *types*. This is a consequence of the fact that in Delphi and
    C, for example, typing is static.

    In a dynamic language like python, however, overloading isn't necessary.
    Not only can the *type* of a function argument be determined at run-time,
    the *number* of arguments can as well.

    Though Alex indicated differently earlier, I intend to always use an "if"
    statment inside one constructor to initialise any class in the situation
    where the arguments may be different in number and type. I don't have the
    years of experience that Alex has, however, so I may end up regretting it
    but right now, it seems to me to be the clearest approach in this
    situation.

    thx
    Caleb
     
    Caleb Hattingh, Feb 7, 2005
    #11
  12. Philip Smith

    Nick Coghlan Guest

    Caleb Hattingh wrote:
    > Though Alex indicated differently earlier, I intend to always use an
    > "if" statment inside one constructor to initialise any class in the
    > situation where the arguments may be different in number and type. I
    > don't have the years of experience that Alex has, however, so I may end
    > up regretting it but right now, it seems to me to be the clearest
    > approach in this situation.


    It varies, and often depends on the *promises* a class can make regarding a set
    of inputs. If you can get a similar effect from a bunch of different types of
    inputs, then putting the functionality all in one method is reasonable.

    On the other hand, if there is a significant semantic difference associated with
    certain inputs, but you *can* do something useful with them, then a separate
    method may be called for.

    The above applies to both ordinary methods and constructors. The builtins and
    the standard library offer many examples of both situations.

    Cheers,
    Nick.

    --
    Nick Coghlan | | Brisbane, Australia
    ---------------------------------------------------------------
    http://boredomandlaziness.skystorm.net
     
    Nick Coghlan, Feb 7, 2005
    #12
  13. Sure, Nick, I agree with you completely.

    I generally try to make sure that my classes are limited in what they
    do/provide, so it is not often a problem that a class may need to be
    instantiated in several very different ways.

    But your point is well taken.

    Thanks
    Caleb
     
    Caleb Hattingh, Feb 8, 2005
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dave Rudolf
    Replies:
    12
    Views:
    8,399
    Martijn Lievaart
    Feb 6, 2004
  2. Jeremy Smith
    Replies:
    2
    Views:
    621
    Jeremy Smith
    Aug 3, 2006
  3. Jess
    Replies:
    5
    Views:
    642
    Ron Natalie
    Jun 7, 2007
  4. Peng Yu
    Replies:
    5
    Views:
    410
    Juha Nieminen
    Sep 19, 2008
  5. srp113
    Replies:
    3
    Views:
    495
Loading...

Share This Page