Lists are one of Python’s most widely used built-in sequence types, serving as ordered, mutable collections that can store heterogeneous elements. This guide explains how to create and initialize lists, traverse and transform them, and apply reliable idioms for common workflows. It covers literal syntax, the list() constructor, comprehension patterns, and methods such as append, extend, sort, and copy, while highlighting performance implications and memory behavior. The content is structured to support both newcomers and experienced developers who want a durable, actionable reference for everyday list use in Python.
Creating Lists: Literals and Constructors
You can create a list using square bracket literals, which is typically the clearest and most concise approach. Inside brackets, separate items with commas, and include elements of any type, including mixed types. The list() constructor can produce a list from an iterable, which is helpful when converting another sequence or an iterator into a list. Both approaches produce a mutable, indexable sequence with dynamic sizing.
Literal syntax
- Empty list: []
- Homogeneous items: [1, 2, 3]
- Heterogeneous items: [1, 'two', 3.0, True]
Constructor from iterables
- From a tuple: list((1, 2, 3))
- From a string: list('ab') yields ['a', 'b']
- From a range: list(range(0, 3)) yields [0, 1, 2]
These patterns are reliable across Python versions and form the foundation for more advanced list construction techniques.
Common Operations and Methods
Lists support a wide set of operations for indexing, slicing, updating, and combining sequences. Indexing lets you access elements by position, including negative indices that count from the end. Slicing creates new lists representing a portion of the original sequence. Lists also offer in-place methods for adding, removing, and reordering elements.
| Operation | Description | Example Result |
|---|---|---|
| len(items) | Number of elements | 3 |
| items + other | Concatenation | [1, 2, 3] + [4] -> [1, 2, 3, 4] |
| items * n | Repetition | [0] * 3 -> [0, 0, 0] |
| items.append(x) | Add element to end | [1].append(2) -> [1, 2] |
| items.extend(iterable) | Extend by iterable elements | [1, 2].extend([3]) -> [1, 2, 3] |
| items.insert(i, x) | Insert at index | [1, 3].insert(1, 2) -> [1, 2, 3] |
| items.remove(x) | Remove first matching value | [1, 2, 1].remove(1) -> [2, 1] |
| del items[i] | Delete by index | del [10, 20, 30][1] -> [10, 30] |
| items.pop([i]) | Remove and return item | [10, 20].pop() -> 20, list becomes [10] |
| items.sort() | In-place sort | [3, 1, 2].sort() -> [1, 2, 3] |
| items.reverse() | Reverse in place | [1, 2, 3].reverse() -> [3, 2, 1] |
| items.copy() | Shallow copy | Copy with list(items) or .copy() |
| in | Membership test | 1 in [1, 2] -> True |
List Comprehensions and Transformations
List comprehensions provide a concise way to create lists by mapping and filtering elements in a single readable expression. A typical pattern is [expression for item in iterable if condition]. You can nest loops within comprehensions to handle two-dimensional data, and you can combine comprehensions with ternary expressions for conditional values. For simpler mappings, map() with list() is an alternative, but comprehensions are generally preferred for clarity in Python.
Examples
- Square of each number: [x * x for x in range(5)]
- Filtered squares: [x * x for x in range(10) if x % 2 == 0]
- Flattening two levels: [y for row in matrix for y in row]
- With condition and ternary: [('even' if x % 2 == 0 else 'odd') for x in range(5)]
Performance and Memory Considerations
Lists are implemented as dynamic arrays, which means appending items is usually fast and amortized O(1), though occasional resizing incurs higher cost. Insertion and deletion at the beginning or middle require shifting elements and are O(n) operations. For large datasets or frequent prepends, collections.deque may be a better choice. Memory usage grows as the list overallocates to accommodate future growth, which is a tradeoff for speed. Being aware of these traits helps you choose the right operations and structures for performance-sensitive code.
Idioms and Best Practices
Writing clear and efficient list code is easier when you rely on idiomatic patterns and avoid common pitfalls. Prefer comprehensions over manual loops for building lists, use in for membership tests instead of indexing when appropriate, and leverage slicing for safe copies and sublists. Avoid mutating a list while iterating over it; instead, iterate over a copy or build a new list. These practices lead to code that is both reliable and maintainable.
Quick reference: list construction and mutation costs
| Pattern | Time complexity | Notes |
|---|---|---|
| [] | O(1) | Instant empty list |
| list(iterable) | O(n) | Full materialization |
| append | Amortized O(1) | Fast add at end |
| insert(0, x) | O(n) | Shift all elements |
| pop(0) | O(n) | Shift all elements |
| sort | O(n log n) | In-place Timsort |
| copy | O(n) | Shallow copy |
Common Gotchas and Pitfalls
Several subtle behaviors can lead to bugs if you’re not careful. Slicing a list produces a shallow copy, which is often what you want for safe mutation. Lists are mutable, so passing them to functions can allow those functions to modify the original list, which may be intentional or surprising. Default arguments like def f(x=[]): create a single list shared across calls and should generally be avoided. Shallow copies share references to nested objects, so mutating nested items can affect multiple parts of your program. Understanding these details helps you avoid unintended side effects.
Integration with Other Data Structures
Lists are often used together with tuples, sets, and dictionaries in real programs. Use tuple unpacking to extract values from lists, and convert to set for deduplication or membership testing when order is not required. You can build dictionaries from lists of keys or pairs using dict(), and you can generate lists of keys or values from dictionaries. These patterns enable smooth transitions between sequence types and help you choose the right structure for each task.
By mastering list creation, operations, and idioms, and by recognizing when a different structure is more appropriate, you can write Python code that is both clear and efficient. The guidance here is designed to remain relevant across Python versions, helping you work confidently with lists in long-term projects.