Data structures and variable bindings are the structural bedrock of all modern software. In this unit, you will master how Python allocates and references objects in heap memory, evaluate primitive scalar types (int, float, complex, bool), leverage immutable vs. mutable sequences (str, list, tuple), harness constant-time lookup collections (set, dict), and inspect types using rigorous runtime validation patterns.
Master arbitrary precision integers, floating point IEEE 754 precision caveats, complex numbers, and boolean truthiness evaluation.
Contrast mutable dynamic arrays (Lists) against fixed memory tuples and immutable text buffers (Strings) with step slicing.
Demystify mathematical Sets and Key-Value Dictionaries operating with O(1) average lookup and hash-table collision resilience.
Distinguish between type() and isinstance() inheritance checks, and perform safe explicit conversions across data boundaries.
In compiled languages like C or Rust, declaring a variable reserves a statically-typed slot of memory on the stack. In Python, everything is an object allocated on the heap. A variable is merely a named pointer (or label) bound to that object.
Python does not possess a native const keyword. By universal convention, developers communicate that a value must never be altered using SCREAMING_SNAKE_CASE. In modern Python 3.8+, static type checkers (like Pylance or Mypy) enforce immutability via the Final qualifier:
from typing import Final
# Standard PEP 8 Convention
DATABASE_TIMEOUT_MS: Final[int] = 5000
MAX_CONCURRENT_SESSIONS: Final[int] = 100
API_BASE_URL: Final[str] = "https://www.devascentacademy.me/api/v1"
# Static type checkers will flag reassignment as an error!
# DATABASE_TIMEOUT_MS = 3000 <-- Pylance: "Cannot assign to Final name"
x, y, z = 10, 20, 30. You can also swap two variables cleanly without an intermediate temporary variable: a, b = b, a!
An identifier is the unique label assigned to variables, functions, classes, and modules. Python enforces strict lexical syntax rules alongside style recommendations:
A-Z), lowercase letters (a-z), numbers (0-9), and underscores (_).1st_item triggers a SyntaxError; item_1 is fully valid).sessionToken, SessionToken, and SESSIONTOKEN represent three completely separate variables in memory.for, class, def, return, yield).| Construct | Casing Convention | Production Example | Anti-Pattern to Avoid |
|---|---|---|---|
| Variables & Functions | snake_case |
student_count, fetch_certificate() |
studentCount, fetchCertificate() |
| Classes | PascalCase (CapWords) |
PaymentGateway, EnrollmentTracker |
payment_gateway |
| Constants | SCREAMING_SNAKE_CASE |
MAX_FILE_SIZE_BYTES |
max_file_size |
| Internal / Private | Single leading underscore (_name) |
_jwt_secret (Module internal) |
Exposing raw secrets publicly |
| Name Mangling | Double leading underscore (__name) |
__private_ledger (Class private) |
Accidental subclass collisions |
for i in range(10):. Never use l (lowercase L), O (uppercase O), or I (uppercase I) as isolated variable names, as they are indistinguishable from digits 1 and 0 in various monospaced fonts!
Python provides three built-in numerical data types optimized for both business computing and scientific simulation:
int) โ Arbitrary PrecisionUnlike C++ or Java where integers overflow at 32 bits (2,147,483,647) or 64 bits, Python 3 integers have unlimited precision! An integer can expand to fill all available system RAM without overflow errors.
# Standard decimal integer
active_students = 10450
# Underscores as visual thousands separators (PEP 515)
budget_paise = 1_000_000_00 # Exactly 10,00,000
# Number systems: Binary (0b), Octal (0o), Hexadecimal (0x)
binary_mask = 0b1010 # Decimal 10
hex_color = 0xFF # Decimal 255
# Arbitrary precision without overflow: 2 to the 100th power!
massive_num = 2 ** 100
print(f"2^100 = {massive_num}")
float)
Floats are implemented as IEEE 754 64-bit double-precision numbers. They handle decimals and scientific notation (e.g. 1.5e-4).
1/3 cannot be represented finitely in decimal (0.3333...), fractions like 0.1 and 0.2 have infinite repeating binary expansions!
0.1 + 0.2 == 0.3 # Returns FALSE (It actually equals 0.30000000000000004)
decimal.Decimal class or store currency as integer paise/cents. For scientific equality, use math.isclose(a, b)!
complex)
Python has native first-class support for complex numbers, widely used in electrical engineering, physics modeling, signal processing, and quantum computing. In Python, the imaginary unit is denoted by j (rather than mathematical i).
# Defined as real + imaginary*j
z1 = 3 + 4j
z2 = complex(2, -1) # 2 - 1j
print(f"Real Part: {z1.real}") # 3.0
print(f"Imaginary Part: {z1.imag}") # 4.0
print(f"Conjugate: {z1.conjugate()}") # (3-4j)
print(f"Magnitude: {abs(z1)}") # 5.0 (Pythagorean: sqrt(3^2 + 4^2))
| Operator | Name | Example | Result | Notes |
|---|---|---|---|---|
+ |
Addition | 10 + 4 |
14 |
Promotes to float if any operand is float |
- |
Subtraction | 10 - 4 |
6 |
Standard negation |
* |
Multiplication | 10 * 4 |
40 |
Also repeats sequences (e.g. '-' * 10) |
/ |
True Division | 10 / 4 |
2.5 |
Always returns float, even for 4 / 2 -> 2.0 |
// |
Floor Division | 10 // 4 |
2 |
Rounds down to nearest integer (truncates towards -infinity) |
% |
Modulus (Remainder) | 10 % 4 |
2 |
Useful for parity checks (num % 2 == 0) |
** |
Exponentiation | 2 ** 3 |
8 |
Power calculation (equivalent to math.pow()) |
In Python 3, strings are immutable sequences of Unicode code points. Because strings are Unicode by default, Python seamlessly handles all global alphabets, accents, and emojis (e.g. ๐, เคนเคฟเคเคฆเฅ, ๆฅๆฌ่ช).
[start:stop:step]
Slicing extracts a substring. The start index is inclusive, while stop is exclusive.
lang = "DevAscent Academy"
print(lang[0:9]) # 'DevAscent' (Indices 0 to 8)
print(lang[10:]) # 'Academy' (Index 10 through end)
print(lang[:3]) # 'Dev' (First 3 characters)
print(lang[-7:]) # 'Academy' (Negative indexing from right)
print(lang[::2]) # 'DvsetAaey' (Every 2nd character)
print(lang[::-1]) # 'ymedacA tnecsaveD' (Reverse string!)
lang[0] = "M" will crash your program with:
TypeError: 'str' object does not support item assignment.
.replace().
Python strings come equipped with rich built-in transformation and inspection methods:
| Method | Description | Example | Returned Output |
|---|---|---|---|
.strip() |
Removes leading and trailing whitespace/newlines | " admin ".strip() |
"admin" |
.lower() / .upper() |
Converts casing for normalization | "DevAscent".lower() |
"devascent" |
.split(sep) |
Breaks string into a list of tokens | "user@domain.com".split("@") |
['user', 'domain.com'] |
.join(iterable) |
Concatenates elements with delimiter | "-".join(["2026", "10", "14"]) |
"2026-10-14" |
.replace(old, new) |
Substitutes all occurrences of substring | "Python 2".replace("2", "3") |
"Python 3" |
.startswith() / .endswith() |
Boolean prefix / suffix verification | url.startswith("https://") |
True or False |
r"...") for Paths and RegEx
By default, Python treats backslashes as escape characters (e.g. \n = newline, \t = tab). Prefixing a string with r disables escape sequence parsing, making it essential for Windows file paths and Regular Expressions:
# Standard string: \t is parsed as a TAB, breaking the path!
broken_path = "C:\new_folder\test.txt"
# Raw string (r"..."): Backslashes are treated as literal characters
valid_path = r"C:\new_folder\test.txt"
print(valid_path) # Output: C:\new_folder\test.txt
The bool data type represents logical truth values: True and False. Under the hood, bool is a direct subclass of int, where True == 1 and False == 0.
and: Returns True only if both operands evaluate to true.or: Returns True if at least one operand evaluates to true.not: Inverts the boolean truth value.
In Python, any object can be tested for truth value inside an if or while condition without explicit comparison to True or False:
| Category | Falsy Values (Evaluates to False) | Truthy Values (Evaluates to True) |
|---|---|---|
| Constants | None, False |
True |
| Numbers | 0, 0.0, 0j |
Any non-zero number (e.g. 1, -42, 3.14) |
| Sequences | Empty string "", empty list [], empty tuple () |
Any populated sequence (e.g. "hello", [0], (False,)) |
| Mappings / Sets | Empty dictionary {}, empty set set() |
Any dictionary or set with at least 1 item |
enrolled_students = []
# Pythonic idiomatic check: Empty list evaluates to Falsy
if not enrolled_students:
print("Cohort is currently empty. Open admissions!")
# Short-circuit evaluation: Default fallback pattern
user_display_name = input("Enter alias: ").strip() or "Anonymous Engineer"
print(f"Logged in as: {user_display_name}")
A List is an ordered, mutable collection that allows duplicate elements and heterogeneous data types. In CPython, lists are implemented as dynamically-sized arrays of contiguous memory pointers.
# Initializing a heterogeneous list
stack = ["FastAPI", "PostgreSQL", "Docker", 2026]
# 1. Appending to end: O(1) amortized
stack.append("Kubernetes")
# 2. Inserting at index: O(n) due to element shifting
stack.insert(0, "Python 3.12")
# 3. Removing by value
stack.remove(2026)
# 4. Popping by index (defaults to last element): O(1)
last_item = stack.pop() # Removes 'Kubernetes'
print(f"Current Production Stack: {stack}")
When a list reaches capacity, CPython does not allocate 1 slot at a time (which would make repeated append() calls prohibitively slow). Instead, it over-allocates extra buffer slots according to the growth formula:
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
This guarantees that appending elements operates with O(1) amortized time complexity.
list.copy() vs. copy.deepcopy())b = a, modifying b alters a because both point to the same memory address.
shallow = a.copy() copies the top-level container, but nested lists still share pointers!
import copy; deep = copy.deepcopy(a).
List comprehensions provide an expressive, high-performance syntax for constructing new lists from existing iterables:
raw_prices_inr = [999, 1499, 2499, 4999, 8999]
# Standard loop vs List Comprehension:
# Syntax: [expression for item in iterable if condition]
discounted = [p * 0.85 for p in raw_prices_inr if p >= 2000]
print(f"Original: {raw_prices_inr}")
print(f"Discounted: {discounted}")
A Tuple is an ordered, immutable sequence. Once created, elements cannot be appended, removed, or replaced in-place.
# 1. Defining a tuple
geo_coordinates = (28.6139, 77.2090) # New Delhi (Lat, Long)
# 2. Tuple Unpacking
latitude, longitude = geo_coordinates
print(f"Lat: {latitude}, Long: {longitude}")
# 3. CRITICAL TRAP: The Single-Element Tuple
fake_tuple = (42) # Evaluates to integer 42, NOT a tuple!
real_tuple = (42,) # Trailing comma is MANDATORY for 1-element tuple!
print(type(fake_tuple)) # <class 'int'>
print(type(real_tuple)) # <class 'tuple'>
from collections import namedtuple. It produces tuple instances accessible via dot notation (e.g. student.name) while retaining immutable zero-overhead performance!
A Set is an unordered collection of unique, hashable elements. Sets automatically deduplicate input data and provide lightning-fast O(1) average time complexity membership tests (item in my_set).
# Automatic Deduplication
raw_cohort_emails = [
"shivan@devascent.me",
"alex@mit.edu",
"shivan@devascent.me", # Duplicate
"priya@iitb.ac.in"
]
unique_students = set(raw_cohort_emails)
print(f"Total Unique Students: {len(unique_students)}")
# Fast membership testing
print("alex@mit.edu" in unique_students) # O(1) Instant Lookup
my_set = {} DOES NOT create an empty set! It creates an empty Dictionary!
my_set = set().
Python sets provide dedicated algebraic operators modeled directly after mathematical Venn diagrams:
| Operation | Operator | Method Equivalent | Venn Diagram Logic |
|---|---|---|---|
| Union | A | B |
A.union(B) |
All elements present in either A or B |
| Intersection | A & B |
A.intersection(B) |
Only elements present in both A and B |
| Difference | A - B |
A.difference(B) |
Elements in A that are not present in B |
| Symmetric Difference | A ^ B |
A.symmetric_difference(B) |
Elements in either A or B, but NOT in both |
frontend_skills = {"HTML", "CSS", "JavaScript", "React"}
backend_skills = {"Python", "FastAPI", "PostgreSQL", "Docker", "JavaScript"}
# Full-stack intersection: Skills shared across both disciplines
shared = frontend_skills & backend_skills
print(f"Shared Skills: {shared}") # {'JavaScript'}
# Backend-exclusive skills
pure_backend = backend_skills - frontend_skills
print(f"Pure Backend: {pure_backend}")
# Complete tech stack union
full_stack = frontend_skills | backend_skills
print(f"Total Unique Skills: {len(full_stack)}")
A Dictionary is an associative mapping of unique keys to arbitrary value objects. Since Python 3.7, standard dictionaries are guaranteed to preserve their insertion order.
Dictionary keys must be immutable and hashable (implementing the __hash__() protocol). You can use str, int, float, or tuple as keys. You cannot use mutable types like list, set, or another dict as keys!
student_profile = {
"id": "STU-1049",
"name": "Aarav Sharma",
"cohort": 14,
"skills": ["Python", "SQL", "FastAPI"],
"is_certified": True,
"gpa": 9.4
}
# 1. Unsafe Access: Throws KeyError if key does not exist!
name = student_profile["name"]
# 2. Safe Access with .get(): Returns default fallback instead of crashing
scholarship = student_profile.get("scholarship_tier", "Standard Tier")
print(f"Scholarship Status: {scholarship}")
# 3. Adding or updating keys
student_profile["grade"] = "A+"
Mastering dictionary iteration patterns is essential for API handling, JSON serialization, and database mappings:
course_enrollments = {
"Python Production": 120,
"AI Engineering": 85,
"Full-Stack Web": 95,
"Cloud Systems": 60
}
# 1. Iterating Keys and Values simultaneously via .items()
for course, count in course_enrollments.items():
print(f"Course: {course:<20} | Enrolled: {count:>3}")
# 2. Dictionary Comprehension: Filter and transform
popular_courses = {
k: v for k, v in course_enrollments.items() if v >= 90
}
print(f"High-Demand Cohorts: {popular_courses}")
combined = default_config | user_overrides. Any key in user_overrides overwrites the duplicate in default_config cleanly!
Python provides two primary mechanisms for inspecting object types at runtime. Choosing the right one distinguishes beginner scripts from enterprise-grade code:
| Feature | type(obj) |
isinstance(obj, classinfo) |
|---|---|---|
| Exact Type Match | Yes (Strict equality only: type(x) is int) |
Yes, but also supports inheritance hierarchies |
| Subclass / OOP Support | NO (Fails to recognize subclasses) | YES (Returns True for derived classes) |
| Multi-Type Check | Requires verbose boolean chaining | Native tuple support: isinstance(x, (int, float)) |
| Industry Recommendation | Avoid for polymorphism | Mandatory Production Standard |
# Demonstrating the Subclass Blindness of type()
flag = True
# Remember: bool is a subclass of int!
print(type(flag) == int) # False (type() is strictly looking for int)
print(isinstance(flag, int)) # True! (Recognizes bool inherits from int)
# Defensive API Input Validation pattern
def calculate_shipping(weight_kg):
if not isinstance(weight_kg, (int, float)):
raise TypeError(f"Weight must be numeric, got {type(weight_kg).__name__}")
if weight_kg <= 0:
raise ValueError("Weight must be strictly greater than 0 kg")
return weight_kg * 150.0
Type conversion refers to changing an entity of one data type into another. Python categorizes conversions into two distinct paradigms:
Python automatically converts one operand into the wider, more expressive type during mathematical evaluations to avoid data truncation:
int_val = 10 # <class 'int'>
float_val = 5.5 # <class 'float'>
result = int_val + float_val
print(f"Result: {result} | Type: {type(result)}")
# Result: 15.5 | Type: <class 'float'> (Automatic promotion to float)
Performed manually by the software engineer using built-in constructor functions:
| Constructor | Target Type | Input Example | Converted Result | Exception Condition |
|---|---|---|---|---|
int(x) |
Integer | int("1400") |
1400 |
ValueError if string contains non-digits |
float(x) |
Float | float("98.5") |
98.5 |
ValueError for malformed text |
str(x) |
String | str(2026) |
"2026" |
Always succeeds for all valid objects |
list(x) |
List | list("ABC") |
['A', 'B', 'C'] |
Fails if input is non-iterable |
set(x) |
Set | set([1, 2, 2, 3]) |
{1, 2, 3} |
Fails if elements are unhashable |
Choosing the optimal data structure directly dictates the performance, scalability, and memory footprint of production backend services:
| Collection | Syntax | Ordered? | Mutable? | Duplicates? | Lookup Time | Ideal Production Use Case |
|---|---|---|---|---|---|---|
| List | [a, b, c] |
Yes | Yes | Yes | O(n) linear scan | Ordered queue, sequential data streams, mutable stacks |
| Tuple | (a, b, c) |
Yes | No (Immutable) | Yes | O(n) linear scan | Fixed configuration records, coordinates, DB rows, dict keys |
| Set | {a, b, c} |
No | Yes | No (Unique) | O(1) Constant | Deduplication, fast membership checks, Venn algebra |
| Dictionary | {k: v} |
Yes (3.7+) | Yes | Keys: No Values: Yes |
O(1) Constant | JSON payloads, database entity caching, symbol lookups |