User defined int class that supports addition - python

I want to create a class that wraps an int and allows some things not normally allowed with int types. Here is my code:
class tInt(int):
def __add__(self, other):
if type(other) == str:
return str(self) + str(other)
elif type(other) == int:
return int(self) + other
elif type(other) == float:
return float(self) + float(other)
else:
return self + other
a = tInt(2)
print (a + "5")
print ("5" + a)
The output was.
>> 25
Traceback (most recent call last):
File "C:\example.py", line 14, in <module>
print ("5" + a)
TypeError: Can't convert 'tInt' object to str implicitly
So, the first print statement ran nicely, and gave what I expected, but the second one gave an error. I think this is because the first one is using tInt's add function because a appeared before + "5" and the second one used the string "5"'s add function first because it appeared first. I know this but I don't really know how to either force a's add function or allow the tInt class to be represented as a string/int/etc.. when a normal type appears before it in an operation.

You need to implement an __radd__ method to handle the case when an instance of your class is on the right hand side of the addition.
The docs say:
These methods are called to implement the binary arithmetic operations
(+, -, *, #, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |) with
reflected (swapped) operands. These functions are only called if the
left operand does not support the corresponding operation and the
operands are of different types. For instance, to evaluate the
expression x - y, where y is an instance of a class that has an
__rsub__() method, y.__rsub__(x) is called if x.__sub__(y) returns NotImplemented.
Example:
class tInt(int):
def __add__(self, other):
if isinstance(other, str):
return str(self) + str(other)
elif isinstance(other, int):
return int(self) + other
elif isinstance(other, float):
return float(self) + float(other)
else:
return NotImplemented
def __radd__(self, other):
return self.__add__(other)
a = tInt(2)
for x in ["5", 5, 5.0]:
print (a + x)
print (x + a)
25
25
7
7
7.0
7.0
As #chepner pointed out in the comments, returning NotImplemented for cases that your method doesn't handle will cause Python to try other ways of performing the operation, or raise a TypeError if there is no way to perform the requested operation.
In the above code, the implementation of __radd__ is trivial because integer addition is associative, that is
2 + 3 == 3 + 2
Addition of other types may not be associative, in which case __radd__ will need to do more than just delegate to __add__:
'a' + 'b' != 'b' + 'a'
[0, 1] + [2, 3] != [2, 3] + [0, 1]

Related

Dividing a number by instances of my class in Python

I have a class called Time, and I need to implement a Frequency class. How can I implement dividing ints or floats by an instance of Time to get an instance of Frequency ?
I already know about __div__, __truediv__, __floordiv__ and other Python special methods, and I already use them in my code to divide instances of classes by numbers or instances of other classes, but I cannot find a way to divide a number by an instance of my class.
Is it possible to implement dividing a number by an instance of a class in Python ?
The __rtruediv__ method is what you're looking for.
When x / y is executed, if type(x) does not implement a __div__(self, other) method where other can be of class type(y), then type(y).__rtruediv__(y, x) is executed, and its result is returned.
Usage:
class Foo:
def __init__(self, x):
self.x = x
def __truediv__(self, other):
return self.x / other
def __rtruediv__(self, other):
return other / self.x
>>> f = Foo(10)
>>> f / 10
1.0
>>> 10 / f
1.0
Yes. You just have to make sure that Time.__rtruediv__() returns a Frequency instance when it receives a float or integer.
Usage:
>>> 100 / Time(2)
Frequency(50.0)
>>> 2.5 / Time(5)
Frequency(0.5)
Implementation:
class Time:
def __init__(self, value):
self.value = value
def __rtruediv__(self, other):
if not isinstance(other, (int, float)):
return NotImplemented
return Frequency(other / self.value)
class Frequency:
def __init__(self, value):
self.value = value
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.value)
The python docs contains a full example on implementing the arithmetic operations for your custom classes.
The proper way to handle incompatible types is to return the special value NotImplemented.
NotImplemented
Special value which should be returned by the binary
special methods (e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.)
to indicate that the operation is not implemented with respect to the
other type
Suppose you try to use a unsupported complex number, returning NotImplemented will eventually cause a TypeError with a correct error message. (at least in python 3)
>>> 100j / Time(2)
Traceback (most recent call last):
File "python", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'complex' and 'Time'
you need to implement __rtruediv__ and__rfloordiv__.
from the documentation
object.__radd__(self, other)
object.__rsub__(self, other)
object.__rmul__(self, other)
object.__rmatmul__(self, other)
object.__rtruediv__(self, other)
object.__rfloordiv__(self, other)
object.__rmod__(self, other)
object.__rdivmod__(self, other)
object.__rpow__(self, other)
object.__rlshift__(self, other)
object.__rrshift__(self, other)
object.__rand__(self, other)
object.__rxor__(self, other)
object.__ror__(self, other)
These methods are called to implement the binary arithmetic operations
(+, -, *, #, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |) with
reflected (swapped) operands. These functions are only called if the
left operand does not support the corresponding operation [3] and the
operands are of different types. [4] For instance, to evaluate the
expression x - y, where y is an instance of a class that has an
__rsub__() method, y.__rsub__(x) is called if x.__sub__(y) returns NotImplemented.

TypeError: unorderable types: Card() < Card() [duplicate]

The following piece of code
class point:
def __init__(self, x, y):
self.x = x
self.y = y
def dispc(self):
return ('(' + str(self.x) + ',' + str(self.y) + ')')
def __cmp__(self, other):
return ((self.x > other.x) and (self.y > other.y))
works fine in Python 2, but in Python 3 I get an error:
>>> p=point(2,3)
>>> q=point(3,4)
>>> p>q
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: point() > point()
It only works for == and !=.
You need to provide the rich comparison methods for ordering in Python 3, which are __lt__, __gt__, __le__, __ge__, __eq__, and __ne__. See also: PEP 207 -- Rich Comparisons.
__cmp__ is no longer used.
More specifically, __lt__ takes self and other as arguments, and needs to return whether self is less than other. For example:
class Point(object):
...
def __lt__(self, other):
return ((self.x < other.x) and (self.y < other.y))
(This isn't a sensible comparison implementation, but it's hard to tell what you were going for.)
So if you have the following situation:
p1 = Point(1, 2)
p2 = Point(3, 4)
p1 < p2
This will be equivalent to:
p1.__lt__(p2)
which would return True.
__eq__ would return True if the points are equal and False otherwise. The other methods work analogously.
If you use the functools.total_ordering decorator, you only need to implement e.g. the __lt__ and __eq__ methods:
from functools import total_ordering
#total_ordering
class Point(object):
def __lt__(self, other):
...
def __eq__(self, other):
...
This was a major and deliberate change in Python 3. See here for more details.
The ordering comparison operators (<, <=, >=, >) raise a TypeError exception when the operands don’t have a meaningful natural ordering. Thus, expressions like 1 < '', 0 > None or len <= len are no longer valid, and e.g. None < None raises TypeError instead of returning False. A corollary is that sorting a heterogeneous list no longer makes sense – all the elements must be comparable to each other. Note that this does not apply to the == and != operators: objects of different incomparable types always compare unequal to each other.
builtin.sorted() and list.sort() no longer accept the cmp argument providing a comparison function. Use the key argument instead. N.B. the key and reverse arguments are now “keyword-only”.
The cmp() function should be treated as gone, and the __cmp__() special method is no longer supported. Use __lt__() for sorting, __eq__() with __hash__(), and other rich comparisons as needed. (If you really need the cmp() functionality, you could use the expression (a > b) - (a < b) as the equivalent for cmp(a, b).)
In Python3 the six rich comparison operators
__lt__(self, other)
__le__(self, other)
__eq__(self, other)
__ne__(self, other)
__gt__(self, other)
__ge__(self, other)
must be provided individually. This can be abbreviated by using functools.total_ordering.
This however turns out rather unreadable and unpractical most of the time. Still you have to put similar code pieces in 2 funcs - or use a further helper func.
So mostly I prefer to use the mixin class PY3__cmp__ shown below. This reestablishes the single __cmp__ method framework, which was and is quite clear and practical in most cases. One can still override selected rich comparisons.
Your example would just become:
class point(PY3__cmp__):
...
# unchanged code
The PY3__cmp__ mixin class:
PY3 = sys.version_info[0] >= 3
if PY3:
def cmp(a, b):
return (a > b) - (a < b)
# mixin class for Python3 supporting __cmp__
class PY3__cmp__:
def __eq__(self, other):
return self.__cmp__(other) == 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __ge__(self, other):
return self.__cmp__(other) >= 0
def __le__(self, other):
return self.__cmp__(other) <= 0
else:
class PY3__cmp__:
pass

Overriding compare operator in Python [duplicate]

The following piece of code
class point:
def __init__(self, x, y):
self.x = x
self.y = y
def dispc(self):
return ('(' + str(self.x) + ',' + str(self.y) + ')')
def __cmp__(self, other):
return ((self.x > other.x) and (self.y > other.y))
works fine in Python 2, but in Python 3 I get an error:
>>> p=point(2,3)
>>> q=point(3,4)
>>> p>q
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: point() > point()
It only works for == and !=.
You need to provide the rich comparison methods for ordering in Python 3, which are __lt__, __gt__, __le__, __ge__, __eq__, and __ne__. See also: PEP 207 -- Rich Comparisons.
__cmp__ is no longer used.
More specifically, __lt__ takes self and other as arguments, and needs to return whether self is less than other. For example:
class Point(object):
...
def __lt__(self, other):
return ((self.x < other.x) and (self.y < other.y))
(This isn't a sensible comparison implementation, but it's hard to tell what you were going for.)
So if you have the following situation:
p1 = Point(1, 2)
p2 = Point(3, 4)
p1 < p2
This will be equivalent to:
p1.__lt__(p2)
which would return True.
__eq__ would return True if the points are equal and False otherwise. The other methods work analogously.
If you use the functools.total_ordering decorator, you only need to implement e.g. the __lt__ and __eq__ methods:
from functools import total_ordering
#total_ordering
class Point(object):
def __lt__(self, other):
...
def __eq__(self, other):
...
This was a major and deliberate change in Python 3. See here for more details.
The ordering comparison operators (<, <=, >=, >) raise a TypeError exception when the operands don’t have a meaningful natural ordering. Thus, expressions like 1 < '', 0 > None or len <= len are no longer valid, and e.g. None < None raises TypeError instead of returning False. A corollary is that sorting a heterogeneous list no longer makes sense – all the elements must be comparable to each other. Note that this does not apply to the == and != operators: objects of different incomparable types always compare unequal to each other.
builtin.sorted() and list.sort() no longer accept the cmp argument providing a comparison function. Use the key argument instead. N.B. the key and reverse arguments are now “keyword-only”.
The cmp() function should be treated as gone, and the __cmp__() special method is no longer supported. Use __lt__() for sorting, __eq__() with __hash__(), and other rich comparisons as needed. (If you really need the cmp() functionality, you could use the expression (a > b) - (a < b) as the equivalent for cmp(a, b).)
In Python3 the six rich comparison operators
__lt__(self, other)
__le__(self, other)
__eq__(self, other)
__ne__(self, other)
__gt__(self, other)
__ge__(self, other)
must be provided individually. This can be abbreviated by using functools.total_ordering.
This however turns out rather unreadable and unpractical most of the time. Still you have to put similar code pieces in 2 funcs - or use a further helper func.
So mostly I prefer to use the mixin class PY3__cmp__ shown below. This reestablishes the single __cmp__ method framework, which was and is quite clear and practical in most cases. One can still override selected rich comparisons.
Your example would just become:
class point(PY3__cmp__):
...
# unchanged code
The PY3__cmp__ mixin class:
PY3 = sys.version_info[0] >= 3
if PY3:
def cmp(a, b):
return (a > b) - (a < b)
# mixin class for Python3 supporting __cmp__
class PY3__cmp__:
def __eq__(self, other):
return self.__cmp__(other) == 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __ge__(self, other):
return self.__cmp__(other) >= 0
def __le__(self, other):
return self.__cmp__(other) <= 0
else:
class PY3__cmp__:
pass

Methods operator overloading

How Python chooses which object to use the method overload?
For example:
class a:
def __init__(self, other):
self.data = other
def __add__(self, other):
return self.data + other
def __radd__(self,other):
return self.data + other
X = a(1)
X+1
1+X
Why in X + 1 expression , calls a method __add__ in object at the left, and in expression 1 + X method __add__ is called at object on the right?
X+1
first, calls:
X.__add__(1)
That succeeds, so no further work is needed.
On the other hand, this:
1+X
calls
(1).__add__(X)
That fails because int doesn't know how to interface with a class a. "As a last resort" this is tried instead:
X.__radd__(1)
From the docs on __radd__:
These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.

Using reverse operators in Python

I have never handled reverse operators before. I just finished learning about them so wanted to try them out. But for some reason, it is not working. Here is the code:
>>> class Subtract(object):
def __init__(self, number):
self.number = number
def __rsub__(self, other):
return self.number - other.number
>>> x = Subtract(5)
>>> y = Subtract(10)
>>> x - y # FAILS!
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
x - y
TypeError: unsupported operand type(s) for -: 'Subtract' and 'Subtract'
>>> x.__rsub__(y) # WORKS!
-5
If I change __rsub__ to __sub__, it works.
What am I doing wrong? Also what is the purpose of these reverse operators?
__rsub__() will only be called if the operands are of different types; when they're of the same type it's assumed that if __sub__ isn't present they can't be subtracted.
Also note that your logic is reversed in any case; you're returning self - other instead of other - self
The point of these methods is to allow this:
class MyNumber(object):
def __init__(self, x):
self.x = x
print 10 - MyNumber(9) # fails because 10.__sub__(MyNumber(9)) is unknown
class MyFixedNumber(MyNumber):
def __rsub__(self, other):
return MyNumber( other - self.x )
print 10 - MyFixedNumber(9) # MyFixedNumber(9).__rsub__(10) is defined
Very rarely useful though, usually you just use things of the same type and the direct __sub__
From Python's Data model at http://docs.python.org/reference/datamodel.html :
These methods are called to implement
the binary arithmetic operations (+,
-, *, /, %, divmod(), pow(), **, <<, >>, &, ^, |) with reflected (swapped) operands. These functions are only
called if the left operand does not
support the corresponding operation
and the operands are of different
types. [2] For instance, to evaluate
the expression x - y, where y is an
instance of a class that has an
__rsub__() method, y.__rsub__(x) is called if x.__sub__(y) returns
NotImplemented.
However - both objects must not be of the same class - that means, that even if you put a __sub__ method returning NotImplemented on your example above, you will still get the same error: Python just assumes your Subtract class can't subtract from "Subtract" iobjects, no matter the order.
However, this works:
class Sub1(object):
number = 5
def __sub__(self, other):
return NotImplemented
class Sub2(object):
number = 2
def __rsub__(self, other):
return other.number - self.number
a = Sub1()
b = Sub2()
print a - b
The methods with reflected operands are provided so that your class can implement an operator when the left operand is a primitive or something else that isn't under your control:
These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.
Since they're both of the same type, it's cowardly refusing and you should implement the __sub__ method.

Categories