23

I'm trying to get a deeper understanding in Python's data model and I don't fully understand the following code:

>>> x = 1

>>> isinstance(x,int)
True

>>> isinstance(x,numbers.Integral)
True

>>> inspect.getmro(int)
(<type 'int'>, <type 'object'>)

>>> inspect.getmro(numbers.Integral)
(<class 'numbers.Integral'>, <class 'numbers.Rational'>, <class 'numbers.Real'>,
 <class 'numbers.Complex'>, <class 'numbers.Number'>, <type 'object'>)

Based on the above, it seems that int and number.Integral are not in the same hierarchy.

From the Python reference (2.6.6) I see

numbers.Integral - These represent elements from the mathematical set of integers (positive and negative).

What's the difference between int and numbers.Integral? Does it have something to do with the type int vs class numbers.Integral I see in the above output?

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
ElenaT
  • 2,600
  • 4
  • 24
  • 41

4 Answers4

11

numbers defines a hierarchy of abstract classes that define operations possible on numeric types. See PEP 3141. The difference between int and Integral is that int is a concrete type that supports all the operations Integral defines.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • I've read about the type hierarchy for numbers and about ABCs. The fact that I don't see `int` and `numbers.Integral` on the same hierarchy is that `int` does not derive from `number.Integral` but it is registered as a "virtual subclass". Correct? I see the relation between them, from `numbers.Integral` towards `int`, in `numbers.Integral._abc_registry` but is it possible to see the other side of the relation also (from `int` towards `numbers.Integral`)? – ElenaT Nov 22 '11 at 21:10
  • 4
    @ElenaT: `number.Integral` is a model (or a concept). `int` is an implementation of that model. That's how they're related. Inheritance is orthogonal to the issue. – Cat Plus Plus Nov 22 '11 at 22:08
  • 1
    I read through PEP 3141, and I still do not understand why issubclass(int, numbers.Integral) returns true, but inspect.getmro(int) does not list Integral as a base class. It seems to me that the method resolution order necessarily includes numbers.Integral. Thus, my only conclusion at this point is some sort of built-in optimization for int(). I was unable to determine the meaning of a Python 'model' or 'concept', but I do see that both int and numbers.Integral are Python classes. – michael Jan 23 '19 at 21:27
5

Allow me to add two things:

isinstance(x,numbers.Integral)

also covers long and

isinstance(x, int)

does not. The numbers.Integral test would be closer to

isinstance(x, (int, long))

in Python 2 (Python 3 killed long for good.)

I prefer the test with numbers.Integral, because if you derive from int (or long), isinstance(y, numbers.Integral) will still be True.

Robert Siemer
  • 32,405
  • 11
  • 84
  • 94
  • 4
    If you subclass `int` (or `long` on Python 2), `isinstance(subclass_instance, (int, long))` would still be true. The difference is if you are working with another Integral type that defines all the same methods but does not inherit from `int` or `long`, such as numpy integers. `isinstance(np.int64(), (int, long)` will always be False, while `isinstance(np.int64(), numbers.Integral)` will return True. – Alex Huszagh Jan 11 '16 at 06:39
5

TLDR: int is registered as a virtual subclass of numbers.Integral.

# numbers.py:380 (CPython 3.8)
Integral.register(int)

numbers.Integral is an abstract definition of what integral numbers must provide. int is a concrete implementation of integral numbers.


The isinstance and issubclass functions are not restricted to inheritance. For example, they can express structural type relations such as collections.abc.Iterable:

>>> class MyIterable:
...     def __iter__(self): ...
...
>>> issubclass(MyIterable, collections.abc.Iterable)
True

In fact, both isinstance and issubclass can be changed for each type. The standard library uses this to define Abstract Base Classes (ABC) support both concrete subclasses (via inheritance) and virtual subclasses (via cls.register(subclass)).

A virtual subclass is not related to its ABC via inheritance -- its method resolution order thus does not use the ABC. In specific, int does not inherit any methods for numbers.Integral. However, it does implement all public methods and operations required for numbers.Integral independently - thus satisfying the numbers.Integral definition.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • Thanks for the answer. One clarification question: Since `numbers.Integral` cannot be instantiated (because it's an ABC), can it even implement any of its methods? – joseville Nov 03 '21 at 18:47
  • ‘However, it does implement all methods required for `numbers.Integral` independently’ Except the `__complex__` method. – Géry Ogam Mar 28 '22 at 11:19
  • @Maggyero I've adjusted the wording to avoid that pitfall. – MisterMiyagi Mar 28 '22 at 11:48
  • `__complex__` is part of the interface so I think it is a bug (it is the only method of the `numbers` ABCs that is not available in the built-in types). – Géry Ogam Mar 28 '22 at 14:24
  • @Maggyero What is and isn't a bug is something to discuss [on the issue tracker](https://bugs.python.org/issue47083), I would say. – MisterMiyagi Mar 28 '22 at 14:28
4
In [34]: numbers.Integral ?
Type:           ABCMeta
Base Class:     <class 'abc.ABCMeta'>
String Form:    <class 'numbers.Integral'>
Namespace:      Interactive
File:           c:\python26\lib\numbers.py
Docstring:
    Integral adds a conversion to long and the bit-string operations.


In [35]: int ?
Type:           type
Base Class:     <type 'type'>
String Form:    <type 'int'>
Namespace:      Python builtin
Docstring:
    int(x[, base]) -> integer


In [36]: type(int) == type (numbers.Integral)
Out[36]: False

In [39]: issubclass(int, numbers.Integral)
Out[39]: True

Integral is an Abstract Base Class. int is a subclass of the ABCMeta Integral

joaquin
  • 82,968
  • 29
  • 138
  • 152