52

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.

25
  • 22
    >"library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer" First thought, something has gone terribly wrong somewhere, and you need to fix that first.... Commented Oct 4, 2016 at 9:40
  • 10
    int(float('Inf')) leads to OverflowError: cannot convert float infinity to integer. There, I did it. I reached stackoverflow. Commented Oct 4, 2016 at 15:14
  • 21
    Related questions: "How to make a boolean that's truer than True", "how to make a file with a filename that is an odor instead of text", "Need a rock that is heavier than itself", "negative size sets" Commented Oct 4, 2016 at 15:29
  • 6
    I love Python. It makes it really easy to accomplish like 5/6 of what you want, even when what you want makes no sense whatsoever. :) Commented Oct 4, 2016 at 15:33
  • 6
    The word "other" in "an integer that's larger than any other integer" means that this is not a contradiction, Bergi. Compare "The barber shaves all other men who do not shave themselves". Commented Oct 4, 2016 at 19:15

7 Answers 7

68

Python integers are unbounded, but you can easily create a subclass of int and override the comparison operators. This provides a way to get an integer instance which which compares as large as any other integer instance:

import functools @functools.total_ordering class NeverSmaller: 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 Python 2, 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 other comparisons can be disabled by implementing __add__ etc to just error out.

Sign up to request clarification or add additional context in comments.

26 Comments

Just quick note: as written something like ReallyMaxInt() + 1 returns 1, ReallyMaxInt() * 1 returns 0, etc etc. Which probably isn't expected.
Well that's the thing, there is no "max value that can fit in a int". You can make bigger int all day until you run out of memory. This is a int in the OO sense, it's just a jacked-up int. If you don't want the math operations to be permitted, you can define them to raise an exception or return NotImplemented.
@RickTeachey Yes, that's an issue when subclassing built-ins. To obtain a "proper" subclass you have to implement all the special methods and the __r*__ variants. In this case it should be quite easy since you can just always return self, so that ReallyMaxInt()+1 == ReallyMaxInt(). But you can automate that a bit with something like: for name in ('add', 'mul', 'pow'): exec('__{0}__ = __r{0}__ = lambda self, other: self'.format(name))
This would work if the "library" is a Python library. If it's a native library, you'll need the maximum value for whatever native type it expects.
@max An int's value is unbounded (except by available memory), which means there is no maximum, but unbounded is different from infinite.
|
27

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.

5 Comments

Nice solution, it gives saner maths e.g. 1/IntInfinity() == 0 is true. If you make them fight, then ReallyMaxInt() > IntInfinity() and vice versa ... let's call it a draw!
Don't inherit from int; inherit from numbers.Integral.
@neilg absolutely that would be correct in general, although in this case it seems the code being called is erroneously testing isinstance against int, not Integral (or Number). Thanks for pointing that out.
@ecatmur We're on the same page. They should file an issue on the project's tracker. It's a bad idea to let one bug become two.
I +1'd you, but I also added an answer that shows how to do this with the ABC. Your solution does weird things when other methods are called, e.g., __int__.
15

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.

4 Comments

Very good point. I didn't think about the fact that I have no idea what else the library does with my integer besides comparison. I think the correct answer to my use case is, if there's no standard infinite integer (which the library presumably is aware of and tested with), there's no safe way to achieve what I want.
@max You also said it's a GUI library, right? (You mentioned window size.) sys.maxint will be larger than any sane window size, even if it's not the largest possible integer in Python. Additionally, do you actually know anything about how the integer is represented in memory in the library? It could, for instance, include some components compiled in C that simply assume that the window size can fit into a native integer value, in which case passing anything larger than that will probably behave badly.
@KyleStrand Agreed. It was a computational window (like rolling sum in an array) rather than GUI window, but they could still make the type of assumptions you describe to optimize code.
@max Then it probably only needs to be bigger than the size of the data, right? If you don't have a bazillion data points then you can pass in a bazillion.
1

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.)

3 Comments

Python was designed intentionally to allow for subclasses to override the behaviour from either side of the operation. From this section of the datamodel: "Note If the right operand’s type is a subclass of the left operand’s type and that subclass provides the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations."
@wim Cool. As I admitted, I've not done much python.
@wim That brings to mind some questions about how Python works under the hood, but that would be a new question. :-)
1

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

3 Comments

Unfortunately, won't work because the argument I'm passing needs to be an int (the library complains if it's not).
@max: then submit a bug on the library's tracker.
In Python, value of an integer can expand to the limit of the available memory.
0

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.

5 Comments

isinstance(x, int) still returns False, so this won't help the user. They've explicitly mentioned that an integer is required, in the question and several times in the comments. I thought you were going to show us a way to have that return True, without inheriting from int?
@wim this is the definition of a Python integral type. If the library explicitly checks isinstance(x, int) then the library is either broken or else they are relying on the representation of an integer in which case, there is no solution.
Remember, practicality beats purity.
@wim I've explained to you that your solution doesn't even satisfy the basic test bed I included. You should at least be able to satisfy the first nine tests that don't do any negation or type conversion.
The only test that matters here is whether it is accepted by the external library, and that's the one you've omitted. You're ignoring the context of the original problem, whilst becoming completely distracted by academic details..
-4

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.