Constructor overloading

  • Thread starter Sergey Krushinsky
  • Start date
S

Sergey Krushinsky

Hello all,

Is there a common way to emulate constructor overloading in Python class?

For instanse, I have 3 classes:
1/ Polar - to hold polar coordinates;
2/ Cartesian - to hold cartesian coordinates;
3/ Coordinates3D, which holds synchronized instances of the both in
__p__ and __c__ fields respectively.

I want to design Coordinates3D so that when instantiated with Polar
argument, self.__p__=argument passed to constructor, and self.__c__ is
calculated. When argument is Cartesian, self.__c__=argument, and
self.__p__ is calculated. Runtime type checking works, but maybe there
is a better way?

Thanks in advance,
Sergey
 
J

John Roth

Sergey Krushinsky said:
Hello all,

Is there a common way to emulate constructor overloading in Python class?

For instanse, I have 3 classes:
1/ Polar - to hold polar coordinates;
2/ Cartesian - to hold cartesian coordinates;
3/ Coordinates3D, which holds synchronized instances of the both in
__p__ and __c__ fields respectively.

I want to design Coordinates3D so that when instantiated with Polar
argument, self.__p__=argument passed to constructor, and self.__c__ is
calculated. When argument is Cartesian, self.__c__=argument, and
self.__p__ is calculated. Runtime type checking works, but maybe there
is a better way?

Depends on what you think is "better." Checking the paramter
types at run time is the clearest way of doing it. The only other
way I know is to use a static method as a constructor, instantiate
an instance of object(), change its class and initialize it to suit
yourself.

The biggest problem with that is that it's totally non-obvious
unless your team does it a lot, and you've got decent naming
conventions so you know what's happening.

John Roth
 
P

Peter Otten

Sergey said:
Hello all,

Is there a common way to emulate constructor overloading in Python class?

For instanse, I have 3 classes:
1/ Polar - to hold polar coordinates;
2/ Cartesian - to hold cartesian coordinates;
3/ Coordinates3D, which holds synchronized instances of the both in
__p__ and __c__ fields respectively.

I want to design Coordinates3D so that when instantiated with Polar
argument, self.__p__=argument passed to constructor, and self.__c__ is
calculated. When argument is Cartesian, self.__c__=argument, and
self.__p__ is calculated. Runtime type checking works, but maybe there
is a better way?

Thanks in advance,
Sergey

Given that Polar and Cartesian could easily grow the missing attributes via
properties, Coordiantes3D seems to be mainly a speed hack and should not
influence the design too much. One approach would be to make a unified
Point class that takes keyword arguments for x, y, z, r, phi, theta and
calculates the missing parameters either immediately in __init__() or
lazily on attribute access (I use 2D examples througout):

class LazyPoint(object):
def getX(self):
try:
return self._x
except AttributeError:
self._x = self.r * math.cos(self.phi)
return self._x
x = property(getX)

Another option would be to add calculated attributes, e. g. x, y, z to
Polar, (without caching) and construct the CachingPoint aka Coordinates3D
using these:

class Point(object):
""" Shared implementation for Polar, Cartesion, and CachingPoint """
r = property(lambda self: self._r)
phi = property(lambda self: self._phi)
x = property(lambda self: self._x)
y = property(lambda self: self._y)
def __str__(self):
return "r=%s, phi=%s, x=%s, y=%s" % \
(self.r, self.phi, self.x, self.y)

class Polar(Point):
def __init__(self, r, phi):
self._r = r
self._phi = phi
x = property(lambda self: self.r * math.cos(self.phi))
y = property(lambda self: self.r * math.sin(self.phi))

class Cartesian(Point):
def __init__(self, x, y):
self._x = x
self._y = y
r = property(lambda self: math.sqrt(self.x*self.x+self.y*self.y))
phi = property(lambda self: math.atan2(self.y, self.x))

class CachingPoint(Point):
def __init__(self, point):
# as both Polar and Cartesion support the full
# attribute set, no type checking is needed here
self._x = point.x
self._y = point.y
self._r = point.r
self._phi = point.phi

if __name__ == "__main__":
p = Polar(1.0, math.pi/4.0)
print p
print CachingPoint(p)
print "---"
p = Cartesian(3.0, 4.0)
print p
print CachingPoint(p)
 
C

Carl Banks

Sergey Krushinsky said:
Hello all,

Is there a common way to emulate constructor overloading in Python class?

For instanse, I have 3 classes:
1/ Polar - to hold polar coordinates;
2/ Cartesian - to hold cartesian coordinates;
3/ Coordinates3D, which holds synchronized instances of the both in
__p__ and __c__ fields respectively.

I want to design Coordinates3D so that when instantiated with Polar
argument, self.__p__=argument passed to constructor, and self.__c__ is
calculated. When argument is Cartesian, self.__c__=argument, and
self.__p__ is calculated. Runtime type checking works, but maybe there
is a better way?


Polar and Cartesian could both have methods cart() and polar().

x.cart() would return the cartesian representation whether x is
Cartesian, Polar, or the Coordinates3D. If x is Polar, it makes the
conversion.

x.polar() would do likewise returning polar representation.

Then, to initialize Coordinates3D, it could set _p and _c thusly:

self._p = x.polar()
self._c = x.cart()

This will work regardless of whether x is a Polar or Cartesian. You
should define these methods for Coordinates3D, too. (Note: you
shouldn't use variables of the form __xxx__ for regular variables; see
Naming Conventions at http://www.python.org/peps/pep-0008.html)

OTOH, I could see why you might not want to do that. If the point of
having three separate classes is so that you use a polar as a polar,
then maybe you wouldn't want to have it return a cartesian. If that's
so, then just check the types. It won't kill you.
 
S

Sergey Krushinsky

Thanks to all :)
I realize that Coordinates3D was not a perfect example, especially in
respect of its design. Anyway, I have learned from your postings some
smart design concepts, which could hardly be implemented in languages
other than Python.

With best regards,
Sergey
 

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,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top