In the previous tutorials text datatypes were explored such as the Immutable Unicode string, Immutable byte string and Mutable byte string the bytearray. These text datatypes were based upon the object class and were seen to have consistent data model identifiers. Python also has a number of numeric datatypes, this tutorial will focus on the immutable integer, which is a full number.
Table of contents
Dot Attributes and the Decimal Point
When the text datatypes were explored a dot . was used to access attributes. For example:
greeting = 'hello'
And inputting:
greeting.upper()
'HELLO'
An attribute can also be accessed directly from an instance without using an instance name:
' '.join(['the', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog'])
'the quick brown fox jumps over the lazy dog'
For numeric datatypes attributes can still be accessed from the instance name:
num = 99
For example:
num.to_bytes()
b'c'
However if a dot attribute is attempted to be used from an integer, the dot is recognised by Python as a decimal point and instead of looking up an attribute for the integer 99, invalid characters are found following the decimal point in the floating point number 99.
99.to_bytes()
SyntaxError: invalid decimal literal
As a consequence to the above, most of the identifiers used with numeric values are data model identifiers which typically map to numeric operators. Details about these identifiers can be seen when examining the help for the int class:
help(int)
Help on class int in module builtins:
class int(object)
 int([x]) > integer
 int(x, base=10) > integer

 Convert a number or string to an integer, or return 0 if no arguments
 are given. If x is a number, return x.__int__(). For floating point
 numbers, this truncates towards zero.

 If x is not a number or if base is given, then x must be a string,
 bytes, or bytearray instance representing an integer literal in the
 given base. The literal can be preceded by '+' or '' and be surrounded
 by whitespace. The base defaults to 10. Valid bases are 0 and 236.
 Base 0 means to interpret the base from the string as an integer literal.
 >>> int('0b100', base=0)
 4

 Builtin subclasses:
 bool

 Methods defined here:

 __abs__(self, /)
 abs(self)

 __add__(self, value, /)
 Return self+value.

 __and__(self, value, /)
 Return self&value.

 __bool__(self, /)
 True if self else False

 __ceil__(...)
 Ceiling of an Integral returns itself.

 __divmod__(self, value, /)
 Return divmod(self, value).

 __eq__(self, value, /)
 Return self==value.

 __float__(self, /)
 float(self)

 __floor__(...)
 Flooring an Integral returns itself.

 __floordiv__(self, value, /)
 Return self//value.

 __format__(self, format_spec, /)
 Default object formatter.

 __ge__(self, value, /)
 Return self>=value.

 __getattribute__(self, name, /)
 Return getattr(self, name).

 __getnewargs__(self, /)

 __gt__(self, value, /)
 Return self>value.

 __hash__(self, /)
 Return hash(self).

 __index__(self, /)
 Return self converted to an integer, if self is suitable for use as an index into a list.

 __int__(self, /)
 int(self)

 __invert__(self, /)
 ~self

 __le__(self, value, /)
 Return self<=value.

 __lshift__(self, value, /)
 Return self<<value.

 __lt__(self, value, /)
 Return self<value.

 __mod__(self, value, /)
 Return self%value.

 __mul__(self, value, /)
 Return self*value.

 __ne__(self, value, /)
 Return self!=value.

 __neg__(self, /)
 self

 __or__(self, value, /)
 Return selfvalue.

 __pos__(self, /)
 +self

 __pow__(self, value, mod=None, /)
 Return pow(self, value, mod).

 __radd__(self, value, /)
 Return value+self.

 __rand__(self, value, /)
 Return value&self.

 __rdivmod__(self, value, /)
 Return divmod(value, self).

 __repr__(self, /)
 Return repr(self).

 __rfloordiv__(self, value, /)
 Return value//self.

 __rlshift__(self, value, /)
 Return value<<self.

 __rmod__(self, value, /)
 Return value%self.

 __rmul__(self, value, /)
 Return value*self.

 __ror__(self, value, /)
 Return valueself.

 __round__(...)
 Rounding an Integral returns itself.

 Rounding with an ndigits argument also returns an integer.

 __rpow__(self, value, mod=None, /)
 Return pow(value, self, mod).

 __rrshift__(self, value, /)
 Return value>>self.

 __rshift__(self, value, /)
 Return self>>value.

 __rsub__(self, value, /)
 Return valueself.

 __rtruediv__(self, value, /)
 Return value/self.

 __rxor__(self, value, /)
 Return value^self.

 __sizeof__(self, /)
 Returns size in memory, in bytes.

 __sub__(self, value, /)
 Return selfvalue.

 __truediv__(self, value, /)
 Return self/value.

 __trunc__(...)
 Truncating an Integral returns itself.

 __xor__(self, value, /)
 Return self^value.

 as_integer_ratio(self, /)
 Return integer ratio.

 Return a pair of integers, whose ratio is exactly equal to the original int
 and with a positive denominator.

 >>> (10).as_integer_ratio()
 (10, 1)
 >>> (10).as_integer_ratio()
 (10, 1)
 >>> (0).as_integer_ratio()
 (0, 1)

 bit_count(self, /)
 Number of ones in the binary representation of the absolute value of self.

 Also known as the population count.

 >>> bin(13)
 '0b1101'
 >>> (13).bit_count()
 3

 bit_length(self, /)
 Number of bits necessary to represent self in binary.

 >>> bin(37)
 '0b100101'
 >>> (37).bit_length()
 6

 conjugate(...)
 Returns self, the complex conjugate of any int.

 to_bytes(self, /, length=1, byteorder='big', *, signed=False)
 Return an array of bytes representing an integer.

 length
 Length of bytes object to use. An OverflowError is raised if the
 integer is not representable with the given number of bytes. Default
 is length 1.
 byteorder
 The byte order used to represent the integer. If byteorder is 'big',
 the most significant byte is at the beginning of the byte array. If
 byteorder is 'little', the most significant byte is at the end of the
 byte array. To request the native byte order of the host system, use
 `sys.byteorder' as the byte order value. Default is to use 'big'.
 signed
 Determines whether two's complement is used to represent the integer.
 If signed is False and a negative integer is given, an OverflowError
 is raised.

 
 Class methods defined here:

 from_bytes(bytes, byteorder='big', *, signed=False) from builtins.type
 Return the integer represented by the given array of bytes.

 bytes
 Holds the array of bytes to convert. The argument must either
 support the buffer protocol or be an iterable object producing bytes.
 Bytes and bytearray are examples of builtin objects that support the
 buffer protocol.
 byteorder
 The byte order used to represent the integer. If byteorder is 'big',
 the most significant byte is at the beginning of the byte array. If
 byteorder is 'little', the most significant byte is at the end of the
 byte array. To request the native byte order of the host system, use
 `sys.byteorder' as the byte order value. Default is to use 'big'.
 signed
 Indicates whether two's complement is used to represent the integer.

 
 Static methods defined here:

 __new__(*args, **kwargs) from builtins.type
 Create and return a new object. See help(type) for accurate signature.

 
 Data descriptors defined here:

 denominator
 the denominator of a rational number in lowest terms

 imag
 the imaginary part of a complex number

 numerator
 the numerator of a rational number in lowest terms

 real
 the real part of a complex number
Notice that the return value shown above for each data model method shows how the function is typically invoked using a Python builtins function or operator.
The __getattribute__ data model defines the behaviour of the getattr builtins function which can be used to retrieve an attribute from an integer:
getattr(99, 'numerator')
99
This can also be used to call a method, were the second set of brackets are used to call the method and provide any input arguments:
getattr(99, 'as_integer_ratio')()
(99, 1)
Initialisation
Inputting int() will display the docstring of the initialisation signature of the integer class as a popup balloon. Some IDEs such as JupyterLab may require the keypress shift ⇧ and tab ↹ to invoke the docstring:
Init signature: int(self, /, *args, **kwargs) Docstring: int([x]) > integer int(x, base=10) > integer Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating point numbers, this truncates towards zero. If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 236. Base 0 means to interpret the base from the string as an integer literal. >>> int('0b100', base=0) 4 Type: type Subclasses: bool, IntEnum, IntFlag, _NamedIntConstant, Handle
An integer can be instantiated using:
num = int(99)
num
99
However since the integer is a fundamental datatype it is usually abbreviated as:
num = 99
num
99
An integer can also be created using a string:
num = int('99', base=10)
num
99
If the base is not specified, it is assumed to be 10:
num = int('99')
num
99
Recall that Hexadecimal values use the base 16.
hex(99)
'0x63'
The hexadecimal string can be cast into an integer using:
num = int('0x63', base=16)
num
99
Identifiers
A list of identifiers can be seen from an integer instance:
 as_integer_ratio
 bit_count
 bit_length
 conjugate
 denominator
 from_bytes
 imag
 numerator
 real
 to_bytes
These are grouped into fraction related identifiers:
 numerator
 denominator
 as_integer_ratio
numerator and denominator are data descriptors which match the integer and are 1 respectively making the integer compatible with the Fraction class:
num.numerator
99
num.denominator
1
The integer ratio returns the numerator and denominator within a tuple:
Signature: num.as_integer_ratio() Docstring: Return integer ratio. Return a pair of integers, whose ratio is exactly equal to the original int and with a positive denominator. >>> (10).as_integer_ratio() (10, 1) >>> (10).as_integer_ratio() (10, 1) >>> (0).as_integer_ratio() (0, 1) Type: builtin_function_or_method
num.as_integer_ratio()
(99, 1)
Complex number related identifiers:
 real
 imag
 conjugate
real and imag are data descriptors which match the integer and are 0 respectively making the integer compatible with the complex class:
num.real
99
num.imag
0
The complex conjugate, returns the real value and reverses the sign of the negative value; since this is 0, the number returned is unchanged:
Docstring: Returns self, the complex conjugate of any int. Type: builtin_function_or_method
num.conjugate()
99
And byte related identifiers:
 from_bytes
 to_bytes
 bit_count
 bit_length
from_bytes is an alternative constructor:
Signature: int.from_bytes(bytes, byteorder='big', *, signed=False) Docstring: Return the integer represented by the given array of bytes. bytes Holds the array of bytes to convert. The argument must either support the buffer protocol or be an iterable object producing bytes. Bytes and bytearray are examples of builtin objects that support the buffer protocol. byteorder The byte order used to represent the integer. If byteorder is 'big', the most significant byte is at the beginning of the byte array. If byteorder is 'little', the most significant byte is at the end of the byte array. To request the native byte order of the host system, use `sys.byteorder' as the byte order value. Default is to use 'big'. signed Indicates whether two's complement is used to represent the integer. Type: builtin_function_or_method
The byte string b'hello' can be converted to an integer using:
int.from_bytes(b'hello', byteorder='big')
448378203247
This can manually be calculated using the ordinal values for each character multiplied by powers of 256 (highest power to the right hand side – big endian):
(ord('h') * 256 ** 4) + (ord('e') * 256 ** 3) + (ord('l') * 256 ** 2) + (ord('l') * 256 ** 1) + (ord('o') * 256 ** 0)
448378203247
For a little byteorder:
int.from_bytes(b'hello', byteorder='little')
478560413032
(ord('h') * 256 ** 0) + (ord('e') * 256 ** 1) + (ord('l') * 256 ** 2) + (ord('l') * 256 ** 3) + (ord('o') * 256 ** 4)
478560413032
An unsigned byte ranges between 0 to 256, the mid value 128 can be examined:
hex(128)
'0x80'
If the byte is signed this range is split between negative and positive values ranging from 128 to +128. The first 128 values (which incorporate the ASCII characters) are unchanged, the next 128 values take on positive values to 256 if unsigned or negative values if signed:
int.from_bytes(b'\x80', byteorder='big', signed=False)
128
int.from_bytes(b'\x80', byteorder='big', signed=True)
128
The to_bytes method is the reverse method and can be used to cast an integer into a bytes:
num = 448378203247
It requires the length f the bytearray to be supplied as an input argument:
Signature: num.to_bytes(length=1, byteorder='big', *, signed=False) Docstring: Return an array of bytes representing an integer. length Length of bytes object to use. An OverflowError is raised if the integer is not representable with the given number of bytes. Default is length 1. byteorder The byte order used to represent the integer. If byteorder is 'big', the most significant byte is at the beginning of the byte array. If byteorder is 'little', the most significant byte is at the end of the byte array. To request the native byte order of the host system, use `sys.byteorder' as the byte order value. Default is to use 'big'. signed Determines whether two's complement is used to represent the integer. If signed is False and a negative integer is given, an OverflowError is raised. Type: builtin_function_or_method
num.to_bytes(length=5, byteorder='big', signed=False)
b'hello'
Trailing zeros will be displayed if the length specified is larger:
num.to_bytes(length=25, byteorder='big', signed=False)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00hello'
An OverflowError will display if the int is too big for the specified length:
num.to_bytes(length=1, byteorder='big', signed=False)
OverflowError: int too big to convert
The bit_length method returns the number of bits required to represent a number in binary:
Signature: num.bit_length() Docstring: Number of bits necessary to represent self in binary. >>> bin(37) '0b100101' >>> (37).bit_length() 6 Type: builtin_function_or_method
For example:
num = 19
The binary representation of the number is:
bin(19)
'0b10011'
There are 5 bits (shown after the 0b prefix):
num.bit_length()
5
The bit_count, returns the number of these bits which have a value of 1:
Signature: num.bit_count() Docstring: Number of ones in the binary representation of the absolute value of self. Also known as the population count. >>> bin(13) '0b1101' >>> (13).bit_count() 3 Type: builtin_function_or_method
In the example 3 of the values are 1:
num.bit_count()
3
Data Model Identifiers
The data model identifier __hash__ is defined for an integer, because an integer is hashable, the hash value is the integer itself:
hash(1)
1
This means an integer can be used as a key in a mapping for example:
mapping = {1: 'one', 2: 'two', 3: 'three'}
And the value can be indexed using the integer key:
mapping[1]
'one'
The data model method __index__ is also defined, meaning an integer can be used to index into a collection such as a Unicode string:
greeting = 'hello'
greeting[1]
'e'
The formal representation of a string __repr__ is also defined:
repr(num)
'19'
Note because only the formal string representation __repr__ is defined, the informal string representation __str__ will behave identically:
str(num)
'19'
The data model __format__ is defined which means the integer can be used in a formatted string, such as an fstring:
f'The number is {num}'
'The number is 19'
f'The number is {num:03d}'
'The number is 019'
f'The number is {num:3d}'
'The number is 19'
The __getnewargs__ data model identifier is used by the pickle module to pickle an integer into a byte string for transfer of data for example over a serial port:
import pickle
pickle.dumps(num)
b'\x80\x04K\x13.'
the pickled byte sequence contains details about the pickle protocol and uses the hex value of the integer:
hex(13)
'0x13'
Unitary Data Model Identifiers
The unitary operator __pos__ defines the behaviour of the + operator. This returns the number unchanged:
+num
19
The unitary operator __neg__ defines the behaviour when the – operator. The returned number has an inverted sign:
num
19
The unitary operator __abs__ defines the behaviour of the builtins function abs. The returned number is stripped of the sign:
abs(num)
19
abs(num)
19
The unitary operator __invert__ returns the ones complement:
~num
20
If the ones complement is assigned to a variable:
complement = ~num
Then num and complement can be converted to bytes and displayed in hex:
num.to_bytes(length=2, byteorder='big', signed=True).hex()
'0013'
inverted.to_bytes(length=2, byteorder='big', signed=True).hex()
'ffec'
Essentially the following hex characters are switched in the hex complement:
hex  hex complement 
0  f 
1  e 
2  d 
3  c 
4  b 
5  a 
6  9 
7  8 
Rounding Data Model Identifiers
The data model identifiers __round__, __floor__, __ceil__, __trunc__ define the behaviour behind the builtins function round and the math functions ceil, floor and trunc. These rounding functions are used to round other numeric data types to an integer and in the case of an integer just return a copy of the integer.
round(num)
19
import math
math.floor(num)
19
math.ceil(num)
19
math.trunc(num)
19
Casting Data Model Identifiers
The __int__ data model identifier defines the behaviour when an integer is cast to an integer using the builtins int class. Since it is originally an integer, the returned integer has the same value:
int(num)
19
The __bool__ data model identifier defines the behaviour when an integer is cast to a bool using the builtins bool class. All nonzero values are True and a zero value is False:
bool(num)
True
The __float__ data model identifier defines the behaviour when an integer is cast to a float using the builtins float class. Notice the inclusion of the decimal point in the output:
float(num)
19.0
Binary Data Model Identifiers
Binary data model identifiers are designed to work between two instances.
Data Model Identifier  Operator or Builtins 
__add__  + 
__sub__  – 
__mul__  * 
__pow__  ** 
__floordiv__  // 
__mod__  % 
__divmod__  divmod 
__truediv__  / 
The instance the operator is called from is known as self and the other instance is known as value. If the data model identifier was to be called directly it would be used with the following syntax:
self.__add__(value)
More typically, the operator is used:
self + value
Notice the instance the method is being called from self is on the left hand side of the operator and the other instance value is on the right hand side. There are a number of reverse data model methods which complement the above and describe what to do when the order is reversed:
Data Model Identifier  Reverse Data Model Identifier 
__add__  __radd__ 
__sub__  __rsub__ 
__mul__  __rmul__ 
__pow__  __rpow__ 
__floordiv__  __rfloordiv__ 
__mod__  __rmod__ 
__divmod__  __rdivmod__ 
__truediv__  __rtruediv__ 
The reverse data model identifiers are typically invoked when the instances surrounding the operator are different class types. For example an integer can be used to replicate a string. The int data model identifier __mul__ will be used for the following operation (self is 2 and is on the left hand side of the * operator):
2 * 'hello'
'hellohello'
The int data model operator __rmul__ will instead be used for the following operation (self is 2 and is on the right hand side of the * operator):
'hello' * 2
'hellohello'
To use these, two instances will need to be created:
num1 = 19
num2 = 52
Addition of two integers can be returned:
num1 + num2
71
Subtraction of two integers can be returned:
num1  num2
33
Multiplication of two integers can be returned:
num1 * num2
988
Exponentiation (raising one integer to the power of another integer) of two integers can be returned:
num1 ** num2
3127427491907749548018497790443751608857168317658177523074947729361
Integer Division of two integers can be returned:
num2 // num1
2
The associated Modulo of two integers can be returned (this is the integer remainder):
num2 % num1
14
And therefore, the following equals num2:
num1 * 2 + 14
52
The divmod of two integers returns a tuple containing the integer division and modulo:
divmod(num2, num1)
(2, 14)
Float Division of two integers will always return a floating point number, even if there is no Modulo with the integer division:
num2 / num1
2.736842105263158
Binary Bitwise Data Model Operators
Data Model Identifier  Operator 
__lshift__  << 
__rshift__  >> 
__and__  & 
__or__   
__xor__  ^ 
The int num1 can be examined in binary:
num1 = 19
bin(num1)
'0b10011'
If it is right shifted by two places, the two least significant digits which were 11 are removed:
bin(num1 >> 2)
'0b100'
This gives the integer:
num1 >> num2
1
If num1 is examined in binary again:
bin(num1)
'0b10011'
And instead is leftshifted by two places
bin(num1 << num2)
'0b1001100'
This gives the integer:
num1 << 2
76
The bitwise operator & will return an integer that has a bit of 1 when both respective bits in the instance num1 and num2 are 1 and is 0 otherwise.
This can be seen when the following two integers are created:
num1 = 201
num2 = 210
This can be seen more clearly by examining the binary sequence for num1 and num2:
bin(num1)
'0b11001001'
bin(num2)
'0b11010010'
Then examining the binary sequence returned from the bitwise and operator:
bin(num1 & num2)
'0b11000000'
In this example on the first two bits are 1 in num1 and num2:
num1 & num2
192
The bitwise operator or will return an integer that has a bit of 1 when either bits in the instance num1 and num2 are 1 and is only 0 when both are 0. The binary sequence of each number can be examined:
bin(num1)
'0b11001001'
bin(num2)
'0b11010010'
Then the bitwise or operator can be examined, only the third and sixth position are 0 as the corresponding bit is 0 in num1 and num2:
bin(num1  num2)
'0b11011011'
This corresponds to the integer:
num1  num2
219
Finally the exclusive or operator will return an integer that has a bit of 1 when the bits in the instance num1 and num2 are different and otherwise has a bit of 0. The binary sequence of each number can be examined:
bin(num1)
'0b11001001'
bin(num2)
'0b11010010'
The first three leading bits are 0 and aren't shown:
bin(num1 ^ num2)
'0b11011'
This corresponds to:
num1 ^ num2
27
These operators also have reverse equivalents:
Data Model Identifier  Reverse Data Model Identifier 
__lshift__  __rlshift__ 
__rshift__  __rrshift__ 
__and__  __rand__ 
__or__  __ror__ 
__xor__  __rxor__ 
Binary Comparison Data Model Identifiers
Since integers are ordinal, the 6 comparison operators are configured:
Data Model Identifier  Operator 
__eq__  == 
__ne__  != 
__gt__  > 
__lt__  < 
__ge__  >= 
__le__  <= 
These 6 comparison operators can be used to compare two integers returning a boolean value of True, if the statement is True, otherwise returning a boolean value of False.
num1 = 19
num2 = 20
A check can be made to see if num1 is equal to num2:
num1 == num2
False
A check can be made to see if num1 is not equal to num2:
num1 != num2
True
A check can be made to see if num1 is greater than num2:
num1 > num2
False
A check can be made to see if num1 is greater than or equal to num2:
num1 >= num2
False
A check can be made to see if num1 is less than num2:
num1 < num2
True
A check can be made to see if num1 is less than or equal to num2:
num1 <= num2
True
PEDMAS
When multiple binary operators are used, a calculation is no carried out from left to right, instead there is a preference for operations in the form of PEDMAS:
Precidence  Operator 
1. Parenthesis  ( ) 
2. Exponentiation  ** 
3. Division  / or // or % 
4. Multiplication  * 
5. Addition  + 
6. Subtraction  – 
The effect of PEDMAS can be seen in the following:
1 + 2  3 * 4 ** 5
3069
This is done using the exponentiation first:
1 + 2  3 * 4 ** 5
Then the multiplication:
1 + 2  3 * 1024
Then the addition and subtraction have the same order of precedence:
1 + 2  3072
This gives the result:
3069
If brackets are added:
(1 + 2  3) * 4 ** 5
0
Then the operation in the brackets is carried out first giving 0 and anything multiplied by 0 is 0.