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.

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
max
  • 49,282
  • 56
  • 208
  • 355
  • 1
    What do you mean 'create an integer that's larger than any other integer'? – Andrew Li Oct 04 '16 at 03:02
  • 3
    I'm just assuming you can't convert `float('inf')` to an `int` because infinity is not a number ;) – MmmHmm Oct 04 '16 at 03:05
  • If you are doing this to find a minimum value in a group of variables, just set the initial value to the first value of the group. – MercyBeaucou Oct 04 '16 at 03:13
  • 1
    @zondo the question you linked doesn't provide an answer to my question. It suggests to use `float('inf')` which doesn't convert to `int`. – max Oct 04 '16 at 03:20
  • @MistahFiggins I'm doing this because of a window function in a third-party library; I essentially want an infinite window size. – max Oct 04 '16 at 03:23
  • @Mr.Kennedy yes of course, I was just pointing out that it can't be done. – max Oct 04 '16 at 03:23
  • 2
    @AndrewL. just an `int` object that returns `True` when compared to any other `int` object using `>`. – max Oct 04 '16 at 03:24
  • 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.... – deworde Oct 04 '16 at 09:40
  • 2
    The library is missing an API which reports the largest integer it currently knows about. If you have that, add 1 to it and pass it into the library. – Kaz Oct 04 '16 at 13:39
  • What do the integers represent? Could a value like, say, 2**256 be a reasonable instance of that represenation? If not, it could serve as a special value. – Kaz Oct 04 '16 at 13:40
  • Related: http://stackoverflow.com/questions/24587994/infinite-integer-in-python – ecatmur Oct 04 '16 at 14:48
  • 10
    `int(float('Inf'))` leads to `OverflowError: cannot convert float infinity to integer`. There, I did it. I reached stackoverflow. – PascalVKooten Oct 04 '16 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" – Jason Orendorff Oct 04 '16 at 15:29
  • Also related: http://stackoverflow.com/questions/10576548/python-usable-max-and-min-values?rq=1 – ecatmur Oct 04 '16 at 15:33
  • 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. :) – Jason Orendorff Oct 04 '16 at 15:33
  • @JasonOrendorff Don't be *daft*, see the first answer. – cat Oct 04 '16 at 16:36
  • Even if this were possible, it would be a terrible idea. The only way to fix this is to change the behaviour of your library function. – TonyK Oct 04 '16 at 19:15
  • 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". – jscs Oct 04 '16 at 19:15
  • @JasonOrendorff See JoshCaswell's comment. I even checked the edit history, and that "other" has been in there since the first revision, so...not really any leeway there. – Kyle Strand Oct 04 '16 at 20:20
  • @Bergi See the last two comments. – Kyle Strand Oct 04 '16 at 20:20
  • 1
    @JoshCaswell Yes - it's the word "integer" or perhaps "python" that makes it a contradiction. Every integer *n* has a successor *n + 1* which is larger. Even in Python, where we can easily break *that* rule (practicality beats mathematical purity, tra la), there is an extremely similar but deeper-rooted rule: every class `X` can have a subclass `Y` which is larger. – Jason Orendorff Oct 04 '16 at 20:22
  • 1
    Anyway - my answer says pretty much how I feel about the whole thing in a less jokey way, didn't mean to mock -- I just like philosophy jokes, that's all. – Jason Orendorff Oct 04 '16 at 20:25
  • @JoshCaswell Every integer has an integer which is larger than it. That's part of how integers are defined. If there is nothing larger than a thing then that thing is not an integer. – user253751 Oct 04 '16 at 22:33
  • 2
    so funny your name is @max and youre asking this – rain_ Oct 05 '16 at 12:01
  • Not sure why people are being such asses about it. Having a max integer is extremely useful, especially in comparisons and error outputs – user3932000 Jul 04 '18 at 15:56

7 Answers7

68

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.

wim
  • 338,267
  • 99
  • 616
  • 750
  • I didn't realize `sys.maxint` wouldn't even work in python 2. – max Oct 04 '16 at 03:34
  • 1
    I'm confused, what the hell is `ReallyMaxInt()` doing anyway? – Havenard Oct 04 '16 at 03:43
  • 2
    @Havenard Just an object that will work wherever an `int` is expected, but will have a special property of comparing larger than any `int`. – max Oct 04 '16 at 03:46
  • 16
    Just quick note: as written something like `ReallyMaxInt() + 1` returns `1`, `ReallyMaxInt() * 1` returns `0`, etc etc. Which probably isn't expected. – Rick Oct 04 '16 at 03:48
  • What I could get from this is that this is basically not really an `int` and doesn't really have the max value that can fit in a int or any int value for that matter, it's simply a class that tricks the `>` operation to always say it's bigger than any other int. That's an interesting approach to the problem I guess. – Havenard Oct 04 '16 at 03:51
  • 24
    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`. – wim Oct 04 '16 at 04:00
  • @wim That's interesting, I didn't know Python had that behavior, usually you have to use a special class to handle numbers bigger than 64-bits. – Havenard Oct 04 '16 at 04:28
  • 5
    @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))` – Bakuriu Oct 04 '16 at 06:46
  • 11
    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. – ivan_pozdeev Oct 04 '16 at 07:10
  • @ivan_pozdeev ahh good point. Completely forgot about C libraries. I guess there's no elegant and universal way to do what I want. Strange that there's no `int('inf')` given that `int` can be indefinitely large. – max Oct 04 '16 at 08:09
  • 8
    @max An `int`'s value is *unbounded* (except by available memory), which means there is no maximum, but *unbounded* is different from *infinite*. – user253751 Oct 04 '16 at 10:07
  • 2
    If I were to write a similar ReallyReallyMaxInt class, which of the two would be bigger? – RemcoGerlich Oct 04 '16 at 13:53
  • 4
    Whichever class's method gets called wins. If you subclass `ReallyMaxInt`, yours is greater (due to details about the way operator overloading works in python). – Jason Orendorff Oct 04 '16 at 15:31
  • If you put two or more `ReallyMaxInt` objects in the same list, which one gets sorted first? – dan04 Oct 04 '16 at 23:51
  • I would presume they would remain in the order they were originally in, since they would compare equal and python's sort is guaranteed stable. Not sure I can prove this, though, maybe Tim Peters can chime in here. – wim Oct 05 '16 at 00:26
  • Don't inherit from `int`; inherit from `numbers.Integral` and define the rest of the interface. – Neil G Oct 07 '16 at 03:27
  • No, it just needs to be a `numbers.Integral`. That is the Python interface for integers. It's poor style for anyone to write `isinstance(int, …)` . – Neil G Oct 07 '16 at 03:55
  • Also, your implementation does weird things when `N` is cast to `int`. – Neil G Oct 07 '16 at 04:00
  • @wim no library function should be checking for `int`. If they are going to check, they can check for `Integral`. Anyway, the problem with your code is that it doesn't work with many of the other integer operations including casting, addition, multiplication, etc. – Neil G Oct 07 '16 at 04:02
  • They're not new. They've been around since Python 2.6. Python 2.6 is already at end of life, so any living Python code should know about them. – Neil G Oct 07 '16 at 04:06
  • That's because `IntEnum` *is* an `int` and `Enum` isn't. ABCs are for when you can only satisfy the interface, but not the representation. Your case of an infinite integer is exactly that. You cannot satisfy the representation of a Python integer, but you can implement most of the interface. – Neil G Oct 07 '16 at 16:41
  • Because `IntEnum` has the same representation as an integer. Your class does not. My answer would be `ecatmur`'s answer with `Integral` substituted for `int`. – Neil G Oct 07 '16 at 18:59
  • Just added it. By the way, your solution fails for `-x < -100` and `x>x` for some reason returns True. – Neil G Oct 07 '16 at 19:07
  • `x>x` should be **false** for any sane definition of infinity — after all you hardcoded `x==x`, so how can `x>x` also be true?! And if `x>100`, then `-x<100`, therefore your code is broken in two ways. (We both understand why it's broken.) I've added a test program to my answer so that you can check your solution. – Neil G Oct 07 '16 at 19:20
  • Look I've explained to you the basics of OO, so I don't know why you're linking them. You should not inherit an implementation that you don't use. You should only inherit the interface. And, you do need to satisfy the requirements I listed: `x >x` must be false especially if `x==x` is true. – Neil G Oct 07 '16 at 19:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/125208/discussion-between-neil-g-and-wim). – Neil G Oct 07 '16 at 20:57
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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 3
    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! – wim Oct 04 '16 at 17:00
  • Don't inherit from `int`; inherit from `numbers.Integral`. – Neil G Oct 07 '16 at 03:27
  • 1
    @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 Oct 07 '16 at 07:42
  • @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. – Neil G Oct 07 '16 at 18:57
  • 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__`. – Neil G Oct 07 '16 at 19:13
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.

Jason Orendorff
  • 42,793
  • 6
  • 62
  • 96
  • 2
    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 Oct 04 '16 at 18:15
  • 2
    @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. – Kyle Strand Oct 04 '16 at 20:24
  • @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 Oct 04 '16 at 20:28
  • @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. – user253751 Oct 04 '16 at 22:36
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.)

Jay
  • 26,876
  • 10
  • 61
  • 112
  • 5
    Python was designed intentionally to allow for *subclasses* to override the behaviour from either side of the operation. [From this section of the datamodel](https://docs.python.org/3/reference/datamodel.html#object.__radd__): "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 Oct 04 '16 at 15:29
  • @wim Cool. As I admitted, I've not done much python. – Jay Oct 04 '16 at 17:32
  • @wim That brings to mind some questions about how Python works under the hood, but that would be a new question. :-) – Jay Oct 05 '16 at 02:44
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

R__raki__
  • 847
  • 4
  • 15
  • 30
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.

Neil G
  • 32,138
  • 39
  • 156
  • 257
  • `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 Oct 07 '16 at 19:23
  • @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. – Neil G Oct 07 '16 at 19:26
  • Remember, practicality beats purity. – wim Oct 07 '16 at 19:41
  • @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. – Neil G Oct 07 '16 at 19:43
  • 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.. – wim Oct 08 '16 at 01:32
-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.

Community
  • 1
  • 1
Rick
  • 43,029
  • 15
  • 76
  • 119