An array stores elements of equal size in memory back to back. These two simple properties explain both its strength and its weakness: indexing is instant, inserting in the middle is expensive. A dynamic array adds a growth mechanism so that append stays cheap.
base + i * size and an array growing from capacity 4 to 8Contiguous memory and O(1) indexing
If an array starts at address 1000 and each element takes 8 bytes, a[5] lies at 1000 + 5 * 8 = 1040. The formula does not depend on n, so a[0] and a[10**6] are read equally fast. A Python list stores references to the elements rather than the elements themselves, and references have equal size, so the same rule works.
Insertion and deletion in the middle: O(n)
Because elements lie without gaps, inserting at position i means shifting every element to its right by one cell. Deleting works the other way, to the left. The number of moves is n - i: inserting at the front is the worst case (n), at the end the best (0).