By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
457,723 Members | 1,223 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 457,723 IT Pros & Developers. It's quick & easy.

Subclassing complex with computed arguments

P: n/a
I want to define a class "point" as a subclass of complex.

When I create an instance

sample = point(<arglist>)

I want "sample" to "be" a complex number, but with its real and
imaginary parts computed in point()'s __init__ function with their
values based on the arglist. I want to compute with point instances
as though they were native complex numbers, but I want to be able to
define some new methods and over-ride some of complex's existing ones.

This is probably very simple, but it hasn't been simple to me.

Please feel free to flame me with RTFM as long as you also tell me
where in TFM to R.

If you respond, please send a copy directly to me. I seldom have
access to this group. (My primary network isn't connected to the
internet.)

Peter
pcolsen-at-comcast.net
Jul 18 '05 #1
Share this Question
Share on Google+
3 Replies


P: n/a
I need a clarification. Does "sample" have to be an instance of complex or
can it be a subclass of complex? So can it be an instance of point as long
as point is a subclass of complex?

If "sample" has to be an instance of complex and not of a subclass, I think
you need to subclass point from complex and override the __new__() method to
return an instance of complex and override __init__() to use all your
arglist. In that case you also need yet another method to actually create
instances of point.

If "sample" can be an instance of a subclass of complex, I think you can
just subclass point from complex and override only __init__().

There is more in the details I guess, but I would like to first see an
answer to the above questions.

Dan

"Peter Olsen" <pc*****@comcast.net> wrote in message
news:a8**************************@posting.google.c om...
I want to define a class "point" as a subclass of complex.

When I create an instance

sample = point(<arglist>)

I want "sample" to "be" a complex number, but with its real and
imaginary parts computed in point()'s __init__ function with their
values based on the arglist. I want to compute with point instances
as though they were native complex numbers, but I want to be able to
define some new methods and over-ride some of complex's existing ones.

This is probably very simple, but it hasn't been simple to me.

Please feel free to flame me with RTFM as long as you also tell me
where in TFM to R.

If you respond, please send a copy directly to me. I seldom have
access to this group. (My primary network isn't connected to the
internet.)

Peter
pcolsen-at-comcast.net

Jul 18 '05 #2

P: n/a
Peter Olsen wrote:
I want to define a class "point" as a subclass of complex.

When I create an instance

sample = point(<arglist>)

I want "sample" to "be" a complex number, but with its real and
imaginary parts computed in point()'s __init__ function with their
values based on the arglist. I want to compute with point instances
as though they were native complex numbers, but I want to be able to
define some new methods and over-ride some of complex's existing ones.


I have come up with two solutions to this. They both have warts.

Disclaimer: I have not extensively tested either of these. In
particular, I have not tested all the numeric operations to make sure
they give correct results!

The first attempt was to subclass complex directly, overriding __new__()
to process the input parameters, and delegating all the numeric special
methods to the base class implementation. The delegation wrapper
converts the returned result back to a point.

The trick to getting this to work was to realize that, for example,
complex.__add__(self, y) requires an actual complex instance for self.
If self is a subclass of complex it returns NotImplemented. So my
delegation wrapper constructs a new complex object before calling the
base class method.

The wart in this implementation is that mixed operations with complex
don't always return a point. If the complex is the left operand,
complex.__op__ is called directly and the result is not wrapped back to
a point.

Here is the implementation:

############## point.py ################
''' A subclass of complex that supports the full range of complex
numeric operations
by subclassing complex.
p=point(1,2)
p point(1+2j) p+1 point(2+2j) 1+p (2+2j) p-1 point(2j)
c=complex(1,2)
p+c point(2+4j)

A point can be used where a complex is expected - though the result
is a complex,
not a point:
import cmath
cmath.sqrt(p) (1.272019649514069+0.78615137775742328j)

This doesn't work correctly - it calls complex.__add__() directly
and the result doesn't get wrapped:
c+p (2+4j)
'''

class point(complex):
def __new__(cls, *args, **kwds):
if len(args) == 2:
# return complex.__new__(cls, args[0]*2, args[1]*2) # Special
args processing goes here
return complex.__new__(cls, args[0], args[1])
return complex.__new__(cls, *args, **kwds)

def __str__(self):
return self.__repr__()

def __repr__(self):
s = complex.__repr__(self)
if s.startswith('('):
return 'point' + s
return 'point(%s)' %s
def makeWrapper(attr):
def wrapper(self, *args):
val = getattr(complex, attr)(complex(self), *args)
# print attr, args, val
return point(val)
return wrapper

for special in ['__abs__', '__add__', '__div__', '__divmod__',
'__floordiv__', '__mod__', '__mul__', '__neg__',
'__pos__', '__pow__', '__radd__', '__rdiv__',
'__rdivmod__', '__reduce__', '__reduce_ex__',
'__rfloordiv__', '__rmod__', '__rmul__',
'__rpow__', '__rsub__', '__rtruediv__',
'__sub__', '__truediv__']:
setattr(point, special, makeWrapper(special))
def _test():
import doctest, point
return doctest.testmod(point)

if __name__ == "__main__":
_test()

###########################################

My second attempt uses delegation rather than subclassing. point
instances have a complex attribute and delegate operations to it.

The trick to getting this one to work was to realize that I had to call
coerce() explicitly in my wrapper function.

The wart in this implementation is that points are not instances of
complex, so to pass a point to e.g. cmath.sqrt(), you have to explicitly
convert to a complex.

I think this implementation is probably safer, since it fails noisily
and failures are easy to fix, whereas the first implementation could
silently give errors (depending on how point and complex actually differ).

Here is the implementation:

################# point2.py ####################

''' A class that supports a full range of complex numeric operations by
delegating
to an instance of complex.
p=point(1,2)
p point(1+2j) p+1 point(2+2j) 1+p point(2+2j) p-1 point(2j) p-4j point(1-2j)
c=complex(1,2)
p+c point(2+4j) c+p point(2+4j)

Since point does not subclass complex, it must be explicitly coerced
to a complex when one is needed:
import cmath
cmath.sqrt(p) Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: a float is required
cmath.sqrt(complex(p))

(1.272019649514069+0.78615137775742328j)
'''

class point(object):
def __init__(self, *args, **kwds):
if len(args) == 1 and isinstance(args[0], complex):
self.c = args[0]
if len(args) == 2:
# self.c = complex(args[0]*2, args[1]*2) # Special args
processing goes here
self.c = complex(args[0], args[1])
else:
self.c = complex(*args, **kwds)

def __str__(self):
return self.__repr__()

def __repr__(self):
s = repr(self.c)
if s.startswith('('):
return 'point' + s
return 'point(%s)' %s

def __complex__(self):
return self.c
def makeWrapper(attr):
def wrapper(self, y):
x, y = coerce(self.c, y)
val = getattr(x, attr)(y)
# print attr, x, y, val
return point(val)
return wrapper
for special in ['__abs__', '__add__', '__div__', '__divmod__',
'__floordiv__', '__mod__', '__mul__', '__neg__',
'__pos__', '__pow__', '__radd__', '__rdiv__',
'__rdivmod__', '__reduce__', '__reduce_ex__',
'__rfloordiv__', '__rmod__', '__rmul__',
'__rpow__', '__rsub__', '__rtruediv__',
'__sub__', '__truediv__']:
setattr(point, special, makeWrapper(special))
def _test():
import doctest, point2
return doctest.testmod(point2)

if __name__ == "__main__":
_test()

####################################

HTH,
Kent
Jul 18 '05 #3

P: n/a
On 25 Nov 2004 14:30:18 -0800, pc*****@comcast.net (Peter Olsen) wrote:
I want to define a class "point" as a subclass of complex.

When I create an instance

sample = point(<arglist>)

I want "sample" to "be" a complex number, but with its real and
imaginary parts computed in point()'s __init__ function with their
values based on the arglist. I want to compute with point instances
as though they were native complex numbers, but I want to be able to
define some new methods and over-ride some of complex's existing ones.

This is probably very simple, but it hasn't been simple to me.

Please feel free to flame me with RTFM as long as you also tell me
where in TFM to R.

If you respond, please send a copy directly to me. I seldom have
access to this group. (My primary network isn't connected to the
internet.)

Well, let us know how it goes, and maybe tell the curious what your
application is ;-). BTW, as others have explained, the initialization
of values is done via complex.__new__, not via __init__. You can override that
too, but I just let it be inherited to do its normal thing.

The following is a way to automate wrapping complex so as to return point instances
from the various operations. Not tested beyond what you see, and it won't
be a speed demon, but might be something to explore your concept with.

If you want to add special methods, you can do it like __repr__ and __coerce__
in the example, or you could add them after. Or you could use a proper metaclass
instead of doing it the function way.

Python 2.4b1 (#56, Nov 3 2004, 01:47:27)
[GCC 3.2.3 (mingw special 20030504-1)] on win32
Type "help", "copyright", "credits" or "license" for more information.
class point(complex): ... """complex wrapper subclass point"""
... def __metaclass__(cname, cbases, cdict):
... P = type(cname, cbases, cdict)
... def mkm(name, func):
... def m(*args): return P(func(*args))
... m.__name__ = name
... return m
... for name, func in complex.__dict__.items():
... if callable(func) and name not in ['__coerce__',
... '__doc__', '__float__', '__getattribute__', '__getnewargs__',
... '__hash__', '__int__', '__long__', '__new__', '__repr__',
... '__str__', 'imag', 'real']:
... setattr(P, name, mkm(name, func))
... def __coerce__(self, other): return self, P(other)
... P.__coerce__ = __coerce__
... def __repr__(self): return '<point %s>'% complex.__repr__(self)
... P.__repr__ = __repr__
... return P
... p = point(3, 4)
p <point (3+4j)> p+1 <point (4+4j)> p-1 <point (2+4j)> 1-p <point (-2-4j)> 1+p <point (4+4j)> p*p <point (-7+24j)> p*p.conjugate() <point (25+0j)> abs(p) <point (5+0j)> print p, type(p), repr(p) (3+4j) <class '__main__.point'> <point (3+4j)> p.imag 4.0 p.real

3.0

You can explore further, and fix the bugs ;-)
I don't know what __getnewargs__ does, so I left it alone to be inherited.

Regards,
Bengt Richter
Jul 18 '05 #4

This discussion thread is closed

Replies have been disabled for this discussion.