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 2-36.
| Base 0 means to interpret the base from the string as an integer literal.
| >>> int('0b100', base=0)
| 4
|
| Built-in 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 self|value.
|
| __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 value|self.
|
| __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 value-self.
|
| __rtruediv__(self, value, /)
| Return value/self.
|
| __rxor__(self, value, /)
| Return value^self.
|
| __sizeof__(self, /)
| Returns size in memory, in bytes.
|
| __sub__(self, value, /)
| Return self-value.
|
| __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 built-in 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 2-36. 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 built-in 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 non-zero 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 left-shifted 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.