Efficiently implementing operator overloads in Python [duplicate] - python

I am in need of a reflected magic method "greater than" and there does not appear to be one. Here is the situation. I have a class which keeps track of units. It is call Property. I have the magic method setup to handle comparisons, but it does not work when I put the Property on the right side. Here is an example:
class Property():
def __init__(self, input, units):
self.value = input
self.units = units
def __gt__(self, other):
if isinstance(other, Property):
return self.value.__gt__(other.value)
else:
return self.value.__gt__(other)
def __float__(self):
return float(self.value)
if __name__=='__main__':
x = Property(1.,'kg')
y = Property(0.,'kg')
print y > x
print float(y) > x
print y > float(x)
So if you run this you will see the output is: False, True, False because the middle example is executing float > Property which uses the built in > not the > I have defined using magic methods. I need a magic method that will be used when the Property is on the right hand side. Is that not a thing? If not, how can I write this so that any combination of values and my own class can be compared. I would like to not have any rules for comparisons. IE, I don't want to just never be able to compare a float to a property.

You can use a functools.total_ordering decorator to create the missing comparison methods for you:
import functools
#functools.total_ordering
class Property():
...
Then you get False, False, False. Do make sure to read its documentation, though.

__lt__ is __gt__'s counterpart; you'll need to implement __lt__. While you're at it, you should probably implement __le__ and __ge__.

Since you've declared the __float__() method, you could always write it as:
print float(y) > float(x)

Related

How to define basic Python structures?

I'm coming to Python from Racket. In Racket, I would define a Point structure like this:
(struct Point (x y) #:transparent)
A point is now a structure with two fields named x and y. I can compare two structures for (deep) equality by calling equal?.
What is the equivalent in Python? It looks to me like I have to write twelve lines:
class Point():
def __init__(self,x,y):
self.x = x;
self.y = y;
def __eq__(self, other):
return ((type(other) is Point)
and self.x == other.x
and self.y == other.y)
def __ne__(self, other):
return not(self == other)
... but surely there's an easier way?
Yes, well, if you need an entire class to represent your data type, then you will have to rely on the __eq__ and related dunder methods. However, in this particular case, a Pythonista would use a namedtuple:
from collections import namedtuple
Point = namedtuple('Point', ['x','y'])
Which will inherit all that from tuple.
If you don't need mutability, the simplest way to make basic classes of this sort is collections.namedtuple:
import collections
Point = collections.namedtuple('Point', 'x y')
That's it. You can just make Point objects with pt = Point(1, 2) or the like, and they work like two-tuples, but they also let you access them via named attributes, e.g. pt.x, pt.y.
The equality checking will be a little looser (Point(1, 2) == (1, 2) evaluates to True, because all namedtuples are subclasses of tuple and will compare using tuple rules, and in fact, different subclasses of tuple that don't override the comparison methods will compare equal to each other if they have the same values), but given that tuples are typically used as anonymous lightweight "classes", this is often what you want.
If you need to customize some behavior (adding functionality, or make the type comparisons stricter), you can make a custom class inherit from a namedtuple to get the basic features for free, then customize the bits you care about, e.g., to prevent it testing equal to non-Point types, you can do:
class Point(collections.namedtuple('PointBase', 'x y')):
def __eq__(self, other):
if not isinstance(other, Point):
return False
return super().__eq__(other)
# Sadly, tuple defines __ne__, so you must override it too to behave properly
# You don't need the canonical __ne__ implementation that handles NotImplemented
# though, since you're explicitly unfriendly to non-Point types
def __ne__(self, other): return not (self == other)

How to make an integer larger than any other integer?

Note: while the accepted answer achieves the result I wanted, and #ecatmur answer provides a more comprehensive option, I feel it's very important to emphasize that my use case is a bad idea in the first place. This is explained very well in #Jason Orendorff answer below.
Note: this question is not a duplicate of the question about sys.maxint. It has nothing to do with sys.maxint; even in python 2 where sys.maxint is available, it does NOT represent largest integer (see the accepted answer).
I need to create an integer that's larger than any other integer, meaning an int object which returns True when compared to any other int object using >. Use case: library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer.
In python 2, I can use sys.maxint (edit: I was wrong). In python 3, math.inf is the closest equivalent, but I can't convert it to int.
Since python integers are unbounded, you have to do this with a custom class:
import functools
#functools.total_ordering
class NeverSmaller(object):
def __le__(self, other):
return False
class ReallyMaxInt(NeverSmaller, int):
def __repr__(self):
return 'ReallyMaxInt()'
Here I've used a mix-in class NeverSmaller rather than direct decoration of ReallyMaxInt, because on Python 3 the action of functools.total_ordering would have been prevented by existing ordering methods inherited from int.
Usage demo:
>>> N = ReallyMaxInt()
>>> N > sys.maxsize
True
>>> isinstance(N, int)
True
>>> sorted([1, N, 0, 9999, sys.maxsize])
[0, 1, 9999, 9223372036854775807, ReallyMaxInt()]
Note that in python2, sys.maxint + 1 is bigger than sys.maxint, so you can't rely on that.
Disclaimer: This is an integer in the OO sense, it is not an integer in the mathematical sense. Consequently, arithmetic operations inherited from the parent class int may not behave sensibly. If this causes any issues for your intended use case, then they can be disabled by implementing __add__ and friends to just error out.
Konsta Vesterinen's infinity.Infinity would work (pypi), except that it doesn't inherit from int, but you can subclass it:
from infinity import Infinity
class IntInfinity(Infinity, int):
pass
assert isinstance(IntInfinity(), int)
assert IntInfinity() > 1e100
Another package that implements "infinity" values is Extremes, which was salvaged from the rejected PEP 326; again, you'd need to subclass from extremes.Max and int.
Use case: library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer.
This sounds like a flaw in the library that should be fixed in its interface. Then all its users would benefit. What library is it?
Creating a magical int subclass with overridden comparison operators might work for you. It's brittle, though; you never know what the library is going to do with that object. Suppose it converts it to a string. What should happen? And data is naturally used in different ways as a library evolves; you may update the library one day to find that your trick doesn't work anymore.
It seems to me that this would be fundamentally impossible. Let's say you write a function that returns this RBI ("really big int"). If the computer is capable of storing it, then someone else could write a function that returns the same value. Is your RBI greater than itself?
Perhaps you can achieve the desired result with something like #wim's answer: Create an object that overrides the comparison operators to make "<" always return false and ">" always return true. (I haven't written a lot of Python. In most object-oriented languages, this would only work if the comparison puts your value first, IF RBI>x. If someone writes the comparison the other way, IF x>RBI, it will fail because the compiler doesn't know how to compare integers to a user-defined class.)
In Python 3.5, you can do:
import math
test = math.inf
And then:
test > 1
test > 10000
test > x
Will always be true. Unless of course, as pointed out, x is also infinity or "nan" ("not a number").
How can I represent an infinite number in Python?
Answered by #WilHall
You should not be inheriting from int unless you want both its interface and its implementation. (Its implementation is an automatically-widening set of bits representing a finite number. You clearly dont' want that.) Since you only want the interface, then inherit from the ABC Integral. Thanks to #ecatmur's answer, we can use infinity to deal with the nitty-gritty of infinity (including negation). Here is how we could combine infinity with the ABC Integral:
import pytest
from infinity import Infinity
from numbers import Integral
class IntegerInfinity(Infinity, Integral):
def __and__(self, other):
raise NotImplementedError
def __ceil__(self):
raise NotImplementedError
def __floor__(self):
raise NotImplementedError
def __int__(self):
raise NotImplementedError
def __invert__(self, other):
raise NotImplementedError
def __lshift__(self, other):
raise NotImplementedError
def __mod__(self, other):
raise NotImplementedError
def __or__(self, other):
raise NotImplementedError
def __rand__(self, other):
raise NotImplementedError
def __rlshift__(self, other):
raise NotImplementedError
def __rmod__(self, other):
raise NotImplementedError
def __ror__(self, other):
raise NotImplementedError
def __round__(self):
raise NotImplementedError
def __rrshift__(self, other):
raise NotImplementedError
def __rshift__(self, other):
raise NotImplementedError
def __rxor__(self, other):
raise NotImplementedError
def __trunc__(self):
raise NotImplementedError
def __xor__(self, other):
raise NotImplementedError
def test():
x = IntegerInfinity()
assert x > 2
assert not x < 3
assert x >= 5
assert not x <= -10
assert x == x
assert not x > x
assert not x < x
assert x >= x
assert x <= x
assert -x == -x
assert -x <= -x
assert -x <= x
assert -x < x
assert -x < -1000
assert not -x < -x
with pytest.raises(Exception):
int(x)
with pytest.raises(Exception):
x | x
with pytest.raises(Exception):
ceil(x)
This can be run with pytest to verify the required invariants.
Another way to do this (very much inspired by wim's answer) might be an object that isn't infinite, but increases on the fly as needed.
Here's what I have in mind:
from functools import wraps
class AlwaysBiggerDesc():
'''A data descriptor that always returns a value bigger than instance._compare'''
def __get__(self, instance, owner):
try:
return instance._compare + 1
except AttributeError:
return instance._val
def __set__(self, instance, value):
try:
del instance._compare
except AttributeError:
pass
instance._val = value
class BiggerThanYou(int):
'''A class that behaves like an integer but that increases as needed so as to be
bigger than "other" values. Defaults to 1 so that instances are considered
to be "truthy" for boolean comparisons.'''
val = AlwaysBiggerDesc()
def __getattribute__(self, name):
f = super().__getattribute__(name)
try:
intf = getattr(int,name)
except AttributeError:
intf = None
if f is intf:
#wraps(f)
def wrapper(*args):
try:
self._compare = args[1]
except IndexError:
self._compare = 0 # Note: 1 will be returned by val descriptor
new_bigger = BiggerThanYou()
try:
new_bigger.val = f(self.val, *args[1:])
except IndexError:
new_bigger.val = f(self.val)
return new_bigger
return wrapper
else:
return f
def __repr__(self):
return 'BiggerThanYou()'
def __str__(self):
return '1000...'
Something like this might avoid a lot of weird behavior that one might not expect. Note that with this kind of approach, if two BiggerThanYou instances are involved in an operation, the LHS would be considered bigger than the RHS.
EDIT: currently this is not working- I'll fix it later. it seems I am being bitten by the special method lookup functionality.

reflected greater than magic methods

I am in need of a reflected magic method "greater than" and there does not appear to be one. Here is the situation. I have a class which keeps track of units. It is call Property. I have the magic method setup to handle comparisons, but it does not work when I put the Property on the right side. Here is an example:
class Property():
def __init__(self, input, units):
self.value = input
self.units = units
def __gt__(self, other):
if isinstance(other, Property):
return self.value.__gt__(other.value)
else:
return self.value.__gt__(other)
def __float__(self):
return float(self.value)
if __name__=='__main__':
x = Property(1.,'kg')
y = Property(0.,'kg')
print y > x
print float(y) > x
print y > float(x)
So if you run this you will see the output is: False, True, False because the middle example is executing float > Property which uses the built in > not the > I have defined using magic methods. I need a magic method that will be used when the Property is on the right hand side. Is that not a thing? If not, how can I write this so that any combination of values and my own class can be compared. I would like to not have any rules for comparisons. IE, I don't want to just never be able to compare a float to a property.
You can use a functools.total_ordering decorator to create the missing comparison methods for you:
import functools
#functools.total_ordering
class Property():
...
Then you get False, False, False. Do make sure to read its documentation, though.
__lt__ is __gt__'s counterpart; you'll need to implement __lt__. While you're at it, you should probably implement __le__ and __ge__.
Since you've declared the __float__() method, you could always write it as:
print float(y) > float(x)

decorator __lt__ and others - why use these and how do they work?

I stumpled into a problem of understanding the decorators. Compared to the code below, what is the use of the __lt__(self, number) method compared to the less_than(self, number) method?
Is there any difference between the two methods and how they handle the arguments?
class Sort:
def __init__(self, number):
self.number = number
def __lt__(self, number):
return self.number < number
def less_than(self, number):
return self.number < number
The two functions, __lt__() and less_than() do the same thing. However, there is a big difference:
When you use the "<" operator, the function __lt__ is called internally by python. So, you can do this
x = Sort(5)
y = Sort(10)
print(x < y)
The comparison x < y calls x.__lt__(y), and so it returns true. In this way you can change the behavior of built in operators for the specific class you have created. See "operator overloading" and "python magic methods" for more details
As Martijn Pieters said this has nothing to do with decorators.
s1 = Sort(1)
s2 = Sort(2)
if s1 < s2:
print 'Yes'
if s1.less_than(2):
print 'Yes'
See the difference?
In the first if you compare to objects of type Sort.
In the second if you invoke a method from instance s1 of type Sort.

Can someone help me understand special methods vs normal methods?

What is the difference between using a special method and just defining a normal class method? I was reading this site which lists a lot of them.
For example it gives a class like this.
class Word(str):
'''Class for words, defining comparison based on word length.'''
def __new__(cls, word):
# Note that we have to use __new__. This is because str is an immutable
# type, so we have to initialize it early (at creation)
if ' ' in word:
print "Value contains spaces. Truncating to first space."
word = word[:word.index(' ')] # Word is now all chars before first space
return str.__new__(cls, word)
def __gt__(self, other):
return len(self) > len(other)
def __lt__(self, other):
return len(self) < len(other)
def __ge__(self, other):
return len(self) >= len(other)
def __le__(self, other):
return len(self) <= len(other)
For each of those special methods why can't I just make a normal method instead, what are they doing different? I think I just need a fundamental explanation that I can't find, thanks.
It is a pythonic way to do this:
word1 = Word('first')
word2 = Word('second')
if word1 > word2:
pass
instead of direct usage of comparator method
NotMagicWord(str):
def is_greater(self, other)
return len(self) > len(other)
word1 = NotMagicWord('first')
word2 = NotMagicWord('second')
if word1.is_greater(word2):
pass
And the same with all other magic method. You define __len__ method to tell python its length using built-in len function, for example. All magic method will be called implicitly while standard operations like binary operators, object calling, comparision and a lot of other. A Guide to Python's Magic Methods is really good, read it and see what behavior you can give to your objects. It similar to operator overloading in C++, if you are familiar with it.
A method like __gt__ is called when you use comparison operators in your code. Writing something like
value1 > value2
Is the equivalent of writing
value1.__gt__(value2)
"Magic methods" are used by Python to implement a lot of its underlying structure.
For example, let's say I have a simple class to represent an (x, y) coordinate pair:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
So, __init__ would be an example of one of these "magic methods" -- it allows me to automatically initialize the class by simply doing Point(3, 2). I could write this without using magic methods by creating my own "init" function, but then I would need to make an explicit method call to initialize my class:
class Point(object):
def init(self, x, y):
self.x = x
self.y = y
return self
p = Point().init(x, y)
Let's take another example -- if I wanted to compare two point variables, I could do:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
This lets me compare two points by doing p1 == p2. In contrast, if I made this a normal eq method, I would have to be more explicit by doing p1.eq(p2).
Basically, magic methods are Python's way of implementing a lot of its syntactic sugar in a way that allows it to be easily customizable by programmers.
For example, I could construct a class that pretends to be a function by implementing __call__:
class Foobar(object):
def __init__(self, a):
self.a = a
def __call__(self, b):
return a + b
f = Foobar(3)
print f(4) # returns 7
Without the magic method, I would have to manually do f.call(4), which means I can no longer pretend the object is a function.
Special methods are handled specially by the rest of the Python language. For example, if you try to compare two Word instances with <, the __lt__ method of Word will be called to determine the result.
The magic methods are called when you use <, ==, > to compare the objects. functools has a helper called total_ordering that will fill in the missing comparison methods if you just define __eq__ and __gt__.
Because str already has all the comparison operations defined, it's necessary to add them as a mixin if you want to take advantage of total_ordering
from functools import total_ordering
#total_ordering
class OrderByLen(object):
def __eq__(self, other):
return len(self) == len(other)
def __gt__(self, other):
return len(self) > len(other)
class Word(OrderByLen, str):
'''Class for words, defining comparison based on word length.'''
def __new__(cls, word):
# Note that we have to use __new__. This is because str is an immutable
# type, so we have to initialize it early (at creation)
if ' ' in word:
print "Value contains spaces. Truncating to first space."
word = word[:word.index(' ')] # Word is now all chars before first space
return str.__new__(cls, word)
print Word('cat') < Word('dog') # False
print Word('cat') > Word('dog') # False
print Word('cat') == Word('dog') # True
print Word('cat') <= Word('elephant') # True
print Word('cat') >= Word('elephant') # False

Categories

Resources