1. Introduction
When we begin learning Python, we usually start with simple data types like integers, lists, or dictionaries, and we learn how to use them in isolation without thinking much about how they relate to each other internally. However, as we progress toward becoming an advanced Python developer, it becomes extremely important to understand how Python organizes all these types into a structured system known as the type hierarchy.
The Python type hierarchy is not just a classification of data types, but a conceptual framework that explains how every object in Python behaves, how different types relate to each other, and how the language itself is designed.
In this tutorial, we will explore this hierarchy in depth, not just by listing types, but by understanding their purpose, their internal behavior, and how they are used in real-world programming.
2. Understanding the Big Picture
Before diving into individual types, it is essential to internalize one fundamental idea:
Everything in Python is an object, and every object belongs to a specific type that determines how it behaves.
This means:
- Numbers are objects
- Functions are objects
- Classes are objects
- Even special values like
Noneare objects
Once you accept this idea, the type hierarchy becomes much easier to understand because it simply organizes these objects into meaningful categories.
3. Numbers in Python
Numbers are one of the most commonly used types in any programming language, and Python provides a rich and flexible system for handling different kinds of numeric data.
3.1 Integral Numbers
Integral numbers represent whole values and are divided into two main types: integers and booleans.
3.1.1 Integers (int)
Integers represent whole numbers, both positive and negative, and unlike many other languages, Python allows integers to grow arbitrarily large without overflow.
a = 10 b = -999999999999999999999999
This flexibility is possible because Python automatically manages memory for integers, allowing them to expand as needed.
3.1.2 Booleans (bool)
At first glance, booleans appear to be a completely separate type used for logical operations, but Python treats them as a special subtype of integers.
x = True y = False print(True + True) # 2 print(False + 10) # 10
This behavior might seem surprising initially, but it reflects Python’s internal design where True is essentially 1 and False is 0. This design allows booleans to seamlessly integrate with arithmetic operations, which can sometimes be very useful in practice.
3.2 Non-Integral Numbers
Non-integral numbers represent values that include fractional or more complex components.
3.2.1 Floating Point Numbers (float)
Floating point numbers are used to represent decimal values.
pi = 3.14 value = 0.1 + 0.2 print(value) # 0.30000000000000004
The unexpected result above occurs because floating point numbers are stored in binary format, which cannot precisely represent certain decimal values.
This is not a Python-specific issue, but rather a limitation of how computers represent real numbers.
3.2.2 Decimal Numbers (Decimal)
When precision is critical, especially in financial applications, Python provides the Decimal type.
from decimal import Decimal
x = Decimal('0.1')
y = Decimal('0.2')
print(x + y) # 0.3
Unlike floats, decimals allow exact representation of numbers, making them suitable for scenarios where rounding errors are unacceptable.
3.2.3 Fractions (Fraction)
Fractions provide an exact representation of rational numbers.
from fractions import Fraction f = Fraction(1, 3) result = f + f + f print(result) # 1
This is particularly important because values like one-third cannot be represented exactly using floats, but fractions preserve their mathematical accuracy.
3.2.3 Complex Numbers
Python also supports complex numbers, which consist of a real and an imaginary part.
c = 2 + 3j print(c.real) # 2.0 print(c.imag) # 3.0
Although complex numbers are not commonly used in everyday applications, they are extremely useful in scientific computing and engineering domains.
4. Collections in Python
Collections are used to store and organize multiple values, and Python provides several types of collections, each designed for specific use cases.
4.1 Sequences
Sequences are ordered collections where elements are stored in a specific order.
4.1.1 Lists (Mutable Sequences)
Lists are one of the most flexible and commonly used data structures.
numbers = [1, 2, 3] numbers.append(4) print(numbers) # [1, 2, 3, 4]
Lists are mutable, which means their contents can be modified after creation, making them suitable for dynamic data.
4.1.2 Tuples (Immutable Sequences)
Tuples are similar to lists but cannot be modified once created.
point = (10, 20)
Because tuples are immutable, they are often used when data integrity is important or when performance optimization is required.
4.1.3 Strings (Immutable Sequences)
Strings are sequences of characters and are also immutable.
text = "Python" print(text[0]) # P
4.2 Sets
Sets are unordered collections of unique elements.
s = {1, 2, 3}
s.add(2)
print(s) # {1, 2, 3}
Sets automatically eliminate duplicates, making them very useful for operations like union, intersection, and membership testing.
4.2.1 Frozen Sets (Immutable Sets)
fs = frozenset([1, 2, 3])
Frozen sets cannot be modified, which makes them suitable for use as dictionary keys or in situations where immutability is required.
4.3 Mappings (Dictionaries)
Dictionaries store data in key-value pairs.
person = {
"name": "James",
"age": 63
}
print(person["name"]) # James
Internally, dictionaries are implemented using hash tables, which allows very fast lookups.
An interesting insight is that sets and dictionaries are closely related in their implementation, as both rely on hashing mechanisms.
5. Callables in Python
5.1 What is a Callable?
A callable is any object that can be invoked using parentheses ().
5.1.1 Functions
def greet(name):
return f"Hello, {name}"
print(greet("James"))
5.1.2 Built-in Functions
length = len([1, 2, 3]) print(length) # 3
5.1.3 Methods
Methods are functions associated with objects.
lst = [1, 2] lst.append(3)
5.1.4 Classes as Callables
Classes themselves are callable because calling a class creates an instance.
class Person:
def __init__(self, name):
self.name = name
p = Person("James")
5.1.5 Callable Objects
You can make objects callable by implementing the __call__ method.
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, value):
return value * self.factor
double = Multiplier(2)
print(double(5)) # 10
5.1.6 Generators
Generators are used for lazy iteration.
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for num in count_up_to(3):
print(num)
6. Singleton Objects
Singleton objects are unique objects that exist only once in memory.
6.1 None
x = None
None represents the absence of a value and always refers to the same object in memory.
6.2 NotImplemented
This is used internally for special operations, especially in operator overloading.
6.3 Ellipsis (...)
x = ...
This is often used as a placeholder or in advanced slicing scenarios.
Putting It All Together
Here is a simplified structure:
Python Objects
│
├── Numbers
│ ├── Integers (int, bool)
│ └── Non-Integers (float, Decimal, Fraction, complex)
│
├── Collections
│ ├── Sequences (list, tuple, str)
│ ├── Sets (set, frozenset)
│ └── Mappings (dict)
│
├── Callables
│ ├── Functions
│ ├── Methods
│ ├── Classes
│ └── Generators
│
└── Singletons
├── None
├── NotImplemented
└── Ellipsis
