How to make an integer larger than any other integer? - python

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.

Related

Efficiently implementing operator overloads in Python [duplicate]

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)

Pythonic way of sorting classes with possible Nones in variables

I have a class that looks more or less like this:
class Something():
def __init__(self,a=None,b=None):
self.a = a
self.b = b
I want to be able to sort it in a list, normally I'd just implement method like this:
def __lt__(self,other):
return (self.a, self.b) < (other.a, other.b)
But this will raise an error in following case:
sort([Something(1,None),Something(1,1)])
While I want is for None values to be treated as greated than or following output:
[Something(1,1),Something(1,None)]
First thing that somes to my mind is change __lt__ to:
def __lt__(self,other):
if self.a and other.a:
if self.a != other.a:
return self.a < other.a
elif self.a is None:
return True
elif other.a is None:
return False
if self.b and other.b:
if self.b != other.b:
return self.b < other.b
elif self.b is None:
return True
return False
This would give me the correct results but its just ugly and python usually has a simpler way, and I don't really want to do it for each variable that I use in sorting of my full class(omitted from here to make problem clearer).
So what is the pythonic way of solving this?
Note
I also tried following but I'm assuming that even better is possible:
This would:
def __lt__(self,other):
sorting_attributes = ['a', 'b']
for attribute in sorting_attributes:
self_value = getattr(self,attribute)
other_value = getattr(other,attribute)
if self_value and other_value:
if self_value != other_value:
return self_value < other_value
elif self_value is None:
return True
elif self_value is None:
return False
Really trying to internalize the Zen of Pyhton and I know that my code is ugly so how do I fix it?
A completely different design I thought of later (posted separately because it's so different it should really be evaluated independently):
Map all your attributes to tuples, where the first element of every tuple is a bool based on the None-ness of the attribute, and the second is the attribute value itself. None/non-None mismatches would short-circuit on the bool representing None-ness preventing the TypeError, everything else would fall back to comparing the good types:
def __lt__(self, other):
def _key(attr):
# Use attr is not None to make None less than everything, is None for greater
return (attr is None, attr)
return (_key(self.a), _key(self.b)) < (_key(other.a), _key(other.b))
Probably slightly slower than my other solution in the case where no None/non-None pair occurs, but much simpler code. It also has the advantage of continuing to raise TypeErrors when mismatched types other than None/non-None arise, rather than potentially misbehaving. I'd definitely call this one my Pythonic solution, even if it is slightly slower in the common case.
An easy way to do this is to convert None to infinity, i.e. float('inf'):
def __lt__(self, other):
def convert(i):
return float('inf') if i is None else i
return [convert(i) for i in (self.a, self.b)] < [convert(i) for i in (other.a, other.b)]
A solution for the general case (where there may not be a convenient "bigger than any value" solution, and you don't want the code to grow more complex as the number of attributes increases), which still operates as fast as possible in the presumed common case of no None values. It does assume TypeError means None was involved, so if you're likely to have mismatched types besides None, this gets more complicated, but frankly, a class design like that is painful to contemplate. This works for any scenario with two or more keys (so attrgetter returns a tuple) and only requires changing the names used to construct the attrgetter to add or remove fields to compare.
def __lt__(self, other, _key=operator.attrgetter('a', 'b')):
# Get the keys once for both inputs efficiently (avoids repeated lookup)
sattrs = _key(self)
oattrs = _key(other)
try:
return sattrs < oattrs # Fast path for no Nones or only paired Nones
except TypeError:
for sattr, oattr in zip(sattrs, oattrs):
# Only care if exactly one is None, because until then, must be equal, or TypeError
# wouldn't occur as we would have short-circuited
if (sattr is None) ^ (oattr is None):
# Exactly one is None, so if it's the right side, self is lesser
return oattr is None
# TypeError implied we should see a mismatch, so assert this to be sure
# we didn't have a non-None related type mismatch
assert False, "TypeError raised, but no None/non-None pair seen
A useful feature of this design is that under no circumstances are rich comparisons invoked for any given attribute more than once; the failed attempt at the fast path proves that there must (assuming invariant of types being either compatible or None golds) be a run of zero or more attribute pairs with equal values, followed by a None/non-None mismatch. Since everything we care about is known equal or a None/non-None mismatch, we don't need to invoke potentially expensive rich comparisons again, we just do cheap identity testing to find the None/non-None mismatch and then return based on which side was None.

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)

How to handle mixed types when implementing comparison operators?

What is the most natural way to complete the following code?
import functools
#functools.total_ordering
class X:
def __init__(self, a):
self._a = a
def __eq__(self, other):
if not isinstance(other, X):
return False
return self._a == other._a
def __lt__(self, other):
if not isinstance(other, X):
return ... // what should go here?
return self._a < other._a
if __name__ == '__main__':
s = [2, 'foo', X(2)]
s.sort()
print s
You can choose whatever feels natural to you; False means your instances always sort after other types, True and they'll be sorted before.
Alternatively, you can return NotImplemented (see the __lt__ and other comparison methods documentation) to signal the comparison is not supported:
def __lt__(self, other):
if not isinstance(other, X):
return NotImplemented
return self._a < other._a
Quoting the documentation:
A rich comparison method may return the singleton NotImplemented if it does not implement the operation for a given pair of arguments. By convention, False and True are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false.
My personal approach:
An exception.
There's no natural order between different types.
The official one: (choose this one, there should be)
Although I don't agree with that completely, the manual clearly states how it should be done:
http://docs.python.org/library/stdtypes.html#comparisons
Objects of different types, except different numeric types and
different string types, never compare equal; such objects are ordered
consistently but arbitrarily (so that sorting a heterogeneous array
yields a consistent result). Furthermore, some types (for example,
file objects) support only a degenerate notion of comparison where any
two objects of that type are unequal. Again, such objects are ordered
arbitrarily but consistently. The <, <=, > and >= operators will raise
a TypeError exception when any operand is a complex number.
So basically... I would raise an exception, but the most pythonic way of doing the ordering would be to comply with the manual.
There should be one-- and preferably only one --obvious way to do it.

overloading augmented arithmetic assignments in python

I'm new to Python so apologies in advance if this is a stupid question.
For an assignment I need to overload augmented arithmetic assignments(+=, -=, /=, *=, **=, %=) for a class myInt. I checked the Python documentation and this is what I came up with:
def __iadd__(self, other):
if isinstance(other, myInt):
self.a += other.a
elif type(other) == int:
self.a += other
else:
raise Exception("invalid argument")
self.a and other.a refer to the int stored in each class instance. I tried testing this out as follows, but each time I get 'None' instead of the expected value 5:
c = myInt(2)
b = myInt(3)
c += b
print c
Can anyone tell me why this is happening? Thanks in advance.
You need to add return self to your method. Explanation:
The semantics of a += b, when type(a) has a special method __iadd__, are defined to be:
a = a.__iadd__(b)
so if __iadd__ returns something different than self, that's what will be bound to name a after the operation. By missing a return statement, the method you posted is equivalent to one with return None.
Augmented operators in Python have to return the final value to be assigned to the name they are called on, usually (and in your case) self. Like all Python methods, missing a return statement implies returning None.
Also,
Never ever ever raise Exception, which is impossible to catch sanely. The code to do so would have to say except Exception, which will catch all exceptions. In this case you want ValueError or TypeError.
Don't typecheck with type(foo) == SomeType. In this (and virtually all) cases, isinstance works better or at least the same.
Whenever you make your own type, like myInt, you should name it with capital letters so people can recognize it as a class name.
Yes, you need "return self", it will look like this:
def __iadd__(self, other):
if isinstance(other, myInt):
self.a += other.a
return self
elif type(other) == int:
self.a += other
return self
else:
raise Exception("invalid argument")

Categories

Resources