subclass of integers

M

Mark Morss

I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

It's simple enough to construct a subclass of integers that behave in
this way:

class Nint(int):
def __add__(self,other):
if (other != None):
return self+other
else:
return None
def __radd__(self,other):
if (other != None):
return other+self
else:
return None
#...and so forth

However I have not been able to figure out how to make it so that
None, as well as an integer, could be an element of my class. My
preliminary impression is that I have to override int.__new__; but I
am uncertain how to do that and have been unable to find anything on
the web explaining that. Indeed I haven't been able to find much
about __new__ at all. Overriding this method of built-in classes
seems to be quite unusual.

I would very much appreciate anyone's help.
 
M

Mark Morss

I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

It's simple enough to construct a subclass of integers that behave in
this way:

class Nint(int):
def __add__(self,other):
if (other != None):
return self+other
else:
return None
def __radd__(self,other):
if (other != None):
return other+self
else:
return None
#...and so forth

However I have not been able to figure out how to make it so that
None, as well as an integer, could be an element of my class. My
preliminary impression is that I have to override int.__new__; but I
am uncertain how to do that and have been unable to find anything on
the web explaining that. Indeed I haven't been able to find much
about __new__ at all. Overriding this method of built-in classes
seems to be quite unusual.

I would very much appreciate anyone's help.

I meant of course that arithmetic operations between integer elements
would return the same result as the corresponding integer operations,
not necessarily addition.
 
Z

Zentrader

I would do something along the lines of the following, although it
only tests for integers and not floats, so would return 'None' for a
float.

class Nint(int):
def __add__(self, x, y):
if isinstance(x, int) and isinstance(y, int):
return x+y
return None

if __name__=='__main__':
N=Nint()
print N.__add__( 1, 2 )
print N.__add__( 1, None )
 
Z

Zentrader

This would accept ints, floats, and decimal types.

import decimal

class Nint(int):
def __add__(self, x, y):
try:
return x+y
except:
return None

if __name__=='__main__':
N=Nint()
print N.__add__( 1, 2 )
print N.__add__( 1, None )
print N.__add__(decimal.Decimal("2"), decimal.Decimal("3"))
print N.__add__(decimal.Decimal("2"), 3)
 
M

Michael Spencer

Mark said:
I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

It's simple enough to construct a subclass of integers that behave in
this way:

class Nint(int):
def __add__(self,other):
if (other != None):
return self+other
else:
return None
def __radd__(self,other):
if (other != None):
return other+self
else:
return None
#...and so forth

However I have not been able to figure out how to make it so that
None, as well as an integer, could be an element of my class. My
preliminary impression is that I have to override int.__new__; but I
am uncertain how to do that and have been unable to find anything on
the web explaining that. Indeed I haven't been able to find much
about __new__ at all. Overriding this method of built-in classes
seems to be quite unusual.

I would very much appreciate anyone's help.
Do you really need to define one class that can represent None and the integers?
integers already behave as you want, except that they cannot do binary
operations with None. So why not simply define a NoneInt object?
... def _all_binops(self, other):
... if isinstance(other, (int, NoneInt)):
... return NoneInt()
... else:
... raise TypeError()
... __add__ = __radd__ = _all_binops
... # ...add comparison, unary methods to taste
... def __repr__(self):
... return "NoneInt()"
... Traceback (most recent call last):
File "<input>", line 1, in <module>

Getting back to your question, you can indeed override int.__new__ to return
something other than a raw int. This explains how and why:
http://www.python.org/download/releases/2.2.3/descrintro/
But unless there's more to your requirement than you set out above, it's
probably not worth the trouble.

HTH, Michael
 
I

Ian Clark

Mark said:
I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

It's simple enough to construct a subclass of integers that behave in
this way:

class Nint(int):
def __add__(self,other):
if (other != None):
return self+other
else:
return None
def __radd__(self,other):
if (other != None):
return other+self
else:
return None
#...and so forth

However I have not been able to figure out how to make it so that
None, as well as an integer, could be an element of my class. My
preliminary impression is that I have to override int.__new__; but I
am uncertain how to do that and have been unable to find anything on
the web explaining that. Indeed I haven't been able to find much
about __new__ at all. Overriding this method of built-in classes
seems to be quite unusual.

I would very much appreciate anyone's help.

My thought would be rather than trying to cram None into a subclass of
int, to use delegation instead...

-----8<--------------------------------------------------
class NoneInt(object):
def __init__(self, value):
self.value = value

def __add__(self, other):
if isinstance(other, NoneInt):
if None in (self.value, other.value):
return NoneInt(None)
return NoneInt(self.value + other.value)
elif isinstance(other, int):
if self.value is None:
return NoneInt(None)
return NoneInt(self.value + other)
else:
raise TypeError(
"unsupported operand type(s) for +: 'NoneInt'"
"and '%s'" % str(other.__class__.__name__)
)
__radd__ = __add__

def __str__(self):
return 'NoneInt(%s)' % str(self.value)


def main():
print '42? ', NoneInt(40) + NoneInt(2)
print '41? ', NoneInt(40) + 1
print '40? ', 25 + NoneInt(15)
print 'None? ', NoneInt(None)
print 'None? ', NoneInt(None) + 1
print 'None? ', 1 + NoneInt(None)
print 'Error? ', NoneInt(0) + 'spam'

if __name__ == '__main__':
main()
-----8<--------------------------------------------------

Ian
 
I

Ian Clark

Ian said:
My thought would be rather than trying to cram None into a subclass of
int, to use delegation instead...

(snip)

Ian

A more robust implementation that accounts for ints/longs as well as
implementing more operations...

-----8<--------------------------------------------------
import operator

class NoneInt(object):
_LEFT = 1
_RIGHT = 2


def __init__(self, value):
self.value = value


def _get_arguments(self, other, direction=_LEFT):
""" Given a direction (left or right), returns the left hand
side and right hand side values. """

if direction == self._LEFT:
lhs = self.value
if isinstance(other, (int, long, type(None))):
rhs = other
else:
rhs = other.value
elif direction == self._RIGHT:
rhs = self.value
if isinstance(other, (int, long, type(None))):
lhs = other
else:
lhs = other.value
else:
raise ValueError('direction must be either _LEFT or _RIGHT')
return (lhs, rhs)


def _operation(op, direction):
""" Given a direction and an operation will return a function
that calls the operation with the arguments in the correct
order. """

def func(self, other):
if not isinstance(other, (int, long, NoneInt, type(None))):
fmt = "unsupported operand type(s) for %s: 'NoneInt'
and '%s'"
args = (op.__name__, other.__class__.__name__)
raise TypeError(fmt % args)

lhs, rhs = self._get_arguments(other, direction)

if None in (lhs, rhs):
return NoneInt(None)
return NoneInt(op(lhs, rhs))
return func


__add__ = _operation(operator.add, _LEFT)
__radd__ = _operation(operator.add, _RIGHT)
__sub__ = _operation(operator.sub, _LEFT)
__rsub__ = _operation(operator.sub, _RIGHT)
__div__ = _operation(operator.div, _LEFT)
__rdiv__ = _operation(operator.div, _RIGHT)
__mul__ = _operation(operator.mul, _LEFT)
__rmul__ = _operation(operator.mul, _RIGHT)
# ... etc


def __eq__(self, other):
lhs, rhs = self._get_arguments(other)
return lhs == rhs


def __nonzero__(self):
return bool(self.value)


def __str__(self):
return 'NoneInt(%s)' % str(self.value)

__repr__ = __str__
-----8<--------------------------------------------------

Ian
 
D

Dan Bishop

I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

Rather than subclassing int, why not just make a singleton NaN object
with overloaded arithmetic operators that all return NaN?
 
G

Gabriel Genellina

Rather than subclassing int, why not just make a singleton NaN object
with overloaded arithmetic operators that all return NaN?

Like this:

class _NaN(object):

__instance = None

def __new__(cls):
if cls.__instance is None:
cls.__instance = super(_NaN, cls).__new__(cls)
return cls.__instance

__repr__ = __str__ = lambda self: 'NaN'

def unop(self): return self
def binop(self, other): return self
def terop(self, other, unused=None): return self
def false2(self, other): return False
def true2(self, other): return True
def notimpl(self): return NotImplemented

__abs__ = __invert__ = __neg__ = __pos__ = unop
__add__ = __and__ = __div__ = __divmod__ = __floordiv__ = __lshift__ =
__mod__ = __mul__ = __rshift__ = __or__ = __sub__ = __truediv__ = __xor__
= binop
__radd__ = __rand__ = __rdiv__ = __rdivmod__ = __rfloordiv__ =
__rlshift__ = __rmod__ = __rmul__ = __rpow__ = __rrshift__ = __ror__ =
__rsub__ = __rtruediv__ = __rxor__ = binop
__pow__ = terop
__lt__ = __le__ = __eq__ = __gt__ = __ge__ = false2
__ne__ = true2

del unop, binop, terop, false2, true2, notimpl

NaN = _NaN()

def test():
assert NaN + 1 is NaN
assert 1 & NaN is NaN
assert NaN * NaN is NaN

assert abs(NaN) is NaN
assert str(NaN)=="NaN"
assert repr(NaN)=="NaN"

assert not (NaN==NaN)
assert (NaN!=NaN)
assert not (NaN>NaN)
assert not (NaN<NaN)
assert not (NaN>=NaN)
assert not (NaN<=NaN)

assert not (NaN==1)
assert (NaN!=1)
assert not (NaN>1)
assert not (NaN<1)
assert not (NaN>=1)
assert not (NaN<=1)

assert not (1==NaN)
assert (1!=NaN)
assert not (1>NaN)
assert not (1<NaN)
assert not (1>=NaN)
assert not (1<=NaN)

assert cmp(NaN, 1)!=0
assert cmp(1, NaN)!=0
#assert cmp(NaN, NaN)!=0

assert NaN is _NaN()
assert NaN is type(NaN)()

test()
 
B

Bruno Desthuilliers

Zentrader a écrit :
This would accept ints, floats, and decimal types.

It doesn't...
import decimal
Useless

class Nint(int):
def __add__(self, x, y):

The prototype for __add__ is __add__(self, other)
try:
return x+y
except:
return None

if __name__=='__main__':
N=Nint()
print N.__add__( 1, 2 )
print N.__add__( 1, None )
print N.__add__(decimal.Decimal("2"), decimal.Decimal("3"))
print N.__add__(decimal.Decimal("2"), 3)

i = Nint(42)
i + 33
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: __add__() takes exactly 3 arguments (2 given)

The following works better (at least for integers - which is what the OP
saked for), but it's still not the solution:
import types

class Nint(int):
def __new__(cls, value=0):
# doesn't work with Nint(None)...
return int.__new__(cls, value)

def __add__(self, other):
if isinstance(other, int):
return int.__add__(self, other)
elif isinstance(other, types.NoneType):
return None
else:
err = "unsupported operand type(s) for +: '%s' and '%s'" \
% (type(self), type(other))
raise TypeError(err)



i = Nint(42)
=> 42
i + None
=> None
i + "allo"
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/tmp/python-8683q3Z", line 19, in __add__
TypeError: unsupported operand type(s) for +: '<class '__main__.Nint'>'
and '<type 'str'>'

Fo some reasons I'm not sure about and didn't have time to investigate
(some guru around ?), trying to catch a TypeError in __add__ failed -
raising a TypeError ! But anyway, this isn't the correct solution since
we don't want Nint.__add__ to return None when other is neither an int
nor None.

Anyway, this still doesn't solves the OP's problem since it doesn't
satisfy isinstance(Nint(None), NoneType). I tried making Nint a subclass
of both int and NoneType, but it doesn't work for obvious reasons (None
is a singleton). The only funny thing here is the error message when
trying to call NoneType.__new__:
Traceback (most recent call last):

<OP>
The best I could come with is:

from types import NoneType

class Nint(int, NoneType):
def __new__(cls, value=None):
# by default, will return Nint(0) even if value is None
return int.__new__(cls, value)

def __add__(self, other):
if isinstance(other, int):
return int.__add__(self, other)
elif isinstance(other, NoneType):
return None
else:
err = "unsupported operand type(s) for +: '%s' and '%s'" \
% (type(self), type(other))
raise TypeError(err)

# other __functions__ to implement, left as an exercise to the OP

__all__ = [Nint]

Maybe some guru around will find something better, but I have some doubts...
</OP>
 
B

Bruno Desthuilliers

Mark Morss a écrit :
I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

It's simple enough to construct a subclass of integers that behave in
this way:

class Nint(int):
def __add__(self,other):
if (other != None):
return self+other
else:
return None
def __radd__(self,other):
if (other != None):
return other+self
else:
return None
#...and so forth

However I have not been able to figure out how to make it so that
None, as well as an integer, could be an element of my class. My
preliminary impression is that I have to override int.__new__; but I
am uncertain how to do that and have been unable to find anything on
the web explaining that. Indeed I haven't been able to find much
about __new__ at all. Overriding this method of built-in classes
seems to be quite unusual.

I would very much appreciate anyone's help.

You'll find documentation in the FineManual(tm):
http://docs.python.org/ref/customization.html
http://www.python.org/download/releases/2.2.3/descrintro/#__new__

and a possible (even if imperfect IMHO) example in my answer to
Zentrader in the thread.

Now would you be kind enough to satisfy my curiousity and explain your
use case ?-)
 
R

Roberto Bonvallet

I would like to construct a class that includes both the integers and
None. I desire that if x and y are elements of this class, and both
are integers, then arithmetic operations between them, such as x+y,
return the same result as integer addition. However if either x or y
is None, these operations return None.

No need to create a new class:

try:
result = a * b
except TypeError:
result = None
 

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,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top