views:

76

answers:

4

Hi, suppose a C++ class has several constructors which are overloaded according the number and type and sequences of their respective parameters, for example, constructor(int x, int y) and constructor(float x, float y, float z), I think these two are overloaded methods, which one to use depends on the parameters, right? So then in python, how could I create a constructor that can work like this? I notice that python has the def method(self, *args, **kwargs):, so can I use it like: def _init_(self, *args), then I check the length of *args, like if len(args) == 2:, then construct according to the 2-parameters constructor, if len(args) == 3, then use the 3-parameters constructor, etc. So, does that work? Or is there any better way to do it with python? Or should I think in other ways that could take the advantage of python feature itself? thanks~

A: 
I think these two are overloaded methods, which one to use depends on the parameters, right? 

Sorry, if I seem to be nitpicking, but just thought of bringing this difference out clearly.

The term parameters and arguments have very specific meaning in C++

argument: an expression in the comma-separated list bounded by the parentheses in a function call expression, a sequence of preprocessing tokens in the comma-separated list bounded by the parentheses in a function-like macro invocation, the operand of throw, or an expression, type-id or template-name in the commaseparated list bounded by the angle brackets in a template instantiation. Also known as an actual argument or actual parameter.

parameter: an object or reference declared as part of a function declaration or definition, or in the catch clause of an exception handler, that acquires a value on entry to the function or handler; an identifier from the commaseparated list bounded by the parentheses immediately following the macro name in a function-like macro definition; or a template-parameter. Parameters are also known as formal arguments or formal parameters

Chubsdad
-1 for not answering the question, +0.1 for being right = -0.9 which rounds to -1 ;)
delnan
thanks, very helpful,it's okay about the -1
ladyfafa
Incidentally, C++ supports rounding towards 0 :) int main(){ double d = -1 + 0.1; int x = d; cout << x; }
Chubsdad
@chubsdad, what is the output? :D
ladyfafa
Oh damn you, C++ :)
delnan
@ladyfafa: The output is 0
Chubsdad
@delnan: Do I still deserve -1?
Chubsdad
@chubsdad: It still doesn't answer the question, so I fear C++'s weird math doesn't save you. And the vote's locked in anyway.
delnan
although i am new here, i found this blazingly interestring
ladyfafa
A: 

This article talks about how a multimethod decorator can be created in Python. I haven't tried out the code that they give, but the syntax that it defines looks quite nice. Here's an example from the article:

from mm import multimethod

@multimethod(int, int)
def foo(a, b):
    ...code for two ints...

@multimethod(float, float):
def foo(a, b):
    ...code for two floats...

@multimethod(str, str):
def foo(a, b):
    ...code for two strings...
David
Yes, this could be a very nice solution to some similar problems, my concern is what if the number of arguments in the constructor vary from each other, not only the types? As said in that article "So what are multimethods? I'll give you my own definition, as I've come to understand them: a function that has multiple versions, distinguished by the type of the arguments."
ladyfafa
@ladyfafa: By the looks of things, you could have versions of a function with different numbers of arguments as well. The decorator they provide appears to just map the given tuple of types (e.g. `(int, int)`) onto `*args` in order to decide which version of the method to call.
David
is there a module called mm in python ?
ladyfafa
@ladyfafa: No, the article that I linked provides the code that would go in that module. It doesn't come with Python, and I haven't looked at the code enough to know whether it's any good. The approach that they've used (decorators) seems like a good one, though.
David
The article is by the BDFL, so I suppose it is good :) I looked at the code and it looks like it does the job *and* would even work under Python 3. Haven't tried it either, though.
delnan
Why the hell was this downvoted?
delnan
Even if this is by GvR, it's *horrible* code. Doing different things depending on the types of the arguments you're given goes against a good chunk of python philosophy. You should *know* what interface your arguments implement; the caller knows, and they shouldn't have to discard that information when they could pick the appropriate function instead.
Aaron Gallagher
@Aaron Gallagher: that article was published in 2005, 5 years passed since then, i mean, was it still prevalent? do you think using "*args" okay? btw, GvR is good
ladyfafa
@Aaron Gallagher: I agree, just look at my answer. But it provides one possible answer to the question, so not much of a reason to downvote it IMHO.
delnan
@ladyfafa, yes, I know who GvR is. It doesn't change the fact that this code sucks. At the time, I wasn't really involved with the python community and basically only doing my own thing, so I couldn't say how prevalent it might've been.
Aaron Gallagher
@delnan, sure, it's one possible answer. It's also a possible answer that will bring nothing but pain and misery to the people who have to maintain or extend the code later.
Aaron Gallagher
+3  A: 

Usually, you're fine with any combination of

  • slightly altered design
  • default arguments (def __init__(self, x = 0.0, y = 0.0, z = 0.0))
  • use of polymorphism (in a duck-typed language, you don't need an overload for SomeThing vs SomeSlightlyDifferentThing if neither inherits from the other one, as long as their interfaces are similar enough).

If that doesn't seem feasible, try harder ;) If it still doesn't seem feasible, look at David's link.

delnan
thanks for the answer
ladyfafa
A: 

It really depends on what you want to do. The args/*kwargs method works fairly well, as does the default arguments that delnan suggests.

The main difference between C++ and Python in this case is the what and why of what you are trying to do. If you have a class that needs floats, just try casting the arguments as floats. You can also rely on default arguments to branch your logic:

class Point(object):
    def __init__(self, x=0.0, y=0.0, z=None):
        # Because None is a singleton, 
        # it's like Highlander - there can be only one! So use 'is'
        # for identity comparison
        if z is None:
            self.x = int(x)
            self.y = int(y)
            self.z = None
        else:
            self.x = float(x)
            self.y = float(y)
            self.z = float(z)

p1 = Point(3, 5)
p2 = Point(1.0, 3.3, 4.2)
p3 = Point('3', '4', '5')
points = [p1, p2, p3]
for p in points:
    print p.x, p.y, p.z

You don't, of course, have to assign self.z = None, that was simply for the convenience of my example.

For the best advice about which pattern to use,

In [17]: import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
...

If your pattern is beautiful, explicit, and simple, it just may be the right one to use.

Wayne Werner