This session is all about Lists → one of the most used data types in Python. We cover what they are, how they live in memory, and every common way to create, access, change, and loop over them, then finish with list comprehensions and zip.

What are Lists

A list lets you store many items under one name. More technically, a list behaves like a dynamic array, which means it can grow and shrink on the fly (you can keep adding items). Without lists you would need a separate variable for every value, which gets messy fast.

marks = [45, 67, 89, 32]   # four values, one name

Array vs List

Many languages have arrays. Python lists are similar but more flexible. The main differences:

1. Fixed vs dynamic size → an array usually has a fixed size, a list can grow or shrink anytime.

2. Convenience (heterogeneous) → an array holds one type only, a list can mix types (int, float, string, even other lists).

3. Speed of execution → arrays are faster for heavy number work (lists are a bit slower).

4. Memory → lists use more memory than a plain array, because of the flexibility they give.

How lists are stored in memory

A list does not store the values directly inside itself. It stores references (memory addresses) that point to the actual objects. We can see this with id(), which gives an object's memory address.

L = [1,2,3]

print(id(L))       # 140163201133376   (address of the list itself)
print(id(L[0]))    # 11126688
print(id(L[1]))    # 11126720
print(id(L[2]))    # 11126752
print(id(1))       # 11126688
print(id(2))       # 11126720
print(id(3))       # 11126752

Notice id(L[0]) and id(1) are the same. So L[0] is not really holding the number 1, it is pointing to the same 1 object sitting elsewhere in memory. (The exact numbers change every run, but the matching pattern stays.)

Characteristics of a List