Списки и кортежи
Списки и кортежи -- основные последовательности в Python. Списки изменяемы, кортежи -- нет. Понимание их различий и правильный выбор -- ключ к эффективному коду.
Списки (list)
Список -- упорядоченная изменяемая коллекция произвольных объектов:
# Creating lists
empty = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, None]
nested = [[1, 2], [3, 4], [5, 6]]
# From other iterables
from_range = list(range(10)) # [0, 1, 2, ..., 9]
from_string = list("Python") # ['P', 'y', 't', 'h', 'o', 'n']
from_tuple = list((1, 2, 3)) # [1, 2, 3]
# Repeat elements
zeros = [0] * 5 # [0, 0, 0, 0, 0]
# WARNING: mutable objects repeat the SAME reference
matrix = [[0] * 3] * 3 # WRONG! All rows are the same object
matrix[0][0] = 1
print(matrix) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] - all rows changed!
# Correct way
matrix = [[0] * 3 for _ in range(3)] # Each row is independent
matrix[0][0] = 1
print(matrix) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
Индексация и срезы
fruits = ["яблоко", "банан", "вишня", "дыня", "ежевика"]
# Indexing
print(fruits[0]) # 'яблоко'
print(fruits[-1]) # 'ежевика'
print(fruits[-2]) # 'дыня'
# Slicing: list[start:stop:step]
print(fruits[1:3]) # ['банан', 'вишня']
print(fruits[:3]) # ['яблоко', 'банан', 'вишня']
print(fruits[2:]) # ['вишня', 'дыня', 'ежевика']
print(fruits[::2]) # ['яблоко', 'вишня', 'ежевика']
print(fruits[::-1]) # reversed list
# Slice assignment (modify multiple elements)
numbers = [0, 1, 2, 3, 4, 5]
numbers[1:4] = [10, 20] # replace 3 elements with 2
print(numbers) # [0, 10, 20, 4, 5]
numbers[2:2] = [99, 98] # insert without removing
print(numbers) # [0, 10, 99, 98, 20, 4, 5]
# Delete via slice
numbers[1:3] = [] # remove elements
print(numbers) # [0, 98, 20, 4, 5]
Методы списков
fruits = ["яблоко", "банан"]
# Adding elements
fruits.append("вишня") # Add to end: ['яблоко', 'банан', 'вишня']
fruits.insert(1, "абрикос") # Insert at index: ['яблоко', 'абрикос', 'банан', 'вишня']
fruits.extend(["дыня", "ежевика"]) # Add multiple elements
# Removing elements
fruits.remove("банан") # Remove first occurrence (ValueError if not found)
last = fruits.pop() # Remove and return last element
second = fruits.pop(1) # Remove and return element at index
fruits.clear() # Remove all elements
# Finding elements
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(numbers.index(4)) # 2 (first index of value)
print(numbers.index(1, 2)) # 3 (search from index 2)
print(numbers.count(1)) # 2 (number of occurrences)
# Sorting
numbers.sort() # Sort in place: [1, 1, 2, 3, 4, 5, 6, 9]
numbers.sort(reverse=True) # Descending: [9, 6, 5, 4, 3, 2, 1, 1]
# Sort by key function
words = ["banana", "apple", "cherry", "date"]
words.sort(key=len) # Sort by length: ['date', 'apple', 'banana', 'cherry']
words.sort(key=str.lower) # Case-insensitive sort
# sorted() creates a new list (does not modify original)
original = [3, 1, 4, 1, 5]
new_sorted = sorted(original)
print(original) # [3, 1, 4, 1, 5] (unchanged)
print(new_sorted) # [1, 1, 3, 4, 5]
# Reversing
numbers = [1, 2, 3, 4, 5]
numbers.reverse() # In place: [5, 4, 3, 2, 1]
reversed_new = list(reversed(numbers)) # New list
# Copy
original = [1, [2, 3], 4]
shallow = original.copy() # Shallow copy (same as original[:])
shallow[1][0] = 99 # Modifies nested list in both!
print(original) # [1, [99, 3], 4]
import copy
deep = copy.deepcopy(original) # Deep copy (fully independent)
deep[1][0] = 0
print(original) # [1, [99, 3], 4] (unchanged)
Список как стек (LIFO)
# Stack: Last In, First Out
stack = []
# Push
stack.append("первый")
stack.append("второй")
stack.append("третий")
print(stack) # ['первый', 'второй', 'третий']
# Pop
top = stack.pop()
print(top) # 'третий'
print(stack) # ['первый', 'второй']
# Peek (look at top without removing)
if stack:
print(stack[-1]) # 'второй'
Кортежи (tuple)
Кортеж -- упорядоченная неизменяемая последовательность:
# Creating tuples
empty = ()
single = (42,) # COMMA is required for single-element tuple!
not_a_tuple = (42) # This is just int 42 with parentheses
pair = (1, 2)
coords = 3.14, 2.71 # Parentheses are optional
mixed = (1, "hello", True)
# From other iterables
from_list = tuple([1, 2, 3])
from_range = tuple(range(5))
# Indexing and slicing (same as lists)
point = (10, 20, 30)
print(point[0]) # 10
print(point[-1]) # 30
print(point[1:]) # (20, 30)
# Tuples are IMMUTABLE
# point[0] = 99 # TypeError!
# But mutable elements inside can change!
data = ([1, 2], [3, 4])
data[0].append(99)
print(data) # ([1, 2, 99], [3, 4])
Распаковка кортежей
# Unpacking
x, y, z = (10, 20, 30)
print(x, y, z) # 10 20 30
# Star unpacking
first, *rest = (1, 2, 3, 4, 5)
print(first) # 1
print(rest) # [2, 3, 4, 5]
*start, last = (1, 2, 3, 4, 5)
print(start) # [1, 2, 3, 4]
print(last) # 5
first, *middle, last = (1, 2, 3, 4, 5)
print(middle) # [2, 3, 4]
# Nested unpacking
data = ("Иван", (25, "Москва"))
name, (age, city) = data
print(f"{name}, {age}, {city}") # Иван, 25, Москва
# Ignore values with _
_, y, _ = (10, 20, 30)
print(y) # 20
# Swap values (tuple unpacking)
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1
Кортежи vs списки
import sys
# Memory: tuples use less memory
lst = [1, 2, 3, 4, 5]
tpl = (1, 2, 3, 4, 5)
print(sys.getsizeof(lst)) # ~96 bytes
print(sys.getsizeof(tpl)) # ~80 bytes
# Performance: tuples are slightly faster
import timeit
print(timeit.timeit('(1,2,3,4,5)', number=1_000_000)) # ~0.01s
print(timeit.timeit('[1,2,3,4,5]', number=1_000_000)) # ~0.05s
# Tuples can be dict keys and set members (hashable)
locations = {(55.75, 37.62): "Москва", (59.93, 30.32): "Петербург"}
# Lists cannot be keys
# {[1, 2]: "value"} # TypeError: unhashable type: 'list'
# When to use which:
# Tuple: fixed structure, return multiple values, dict keys, immutable data
# List: dynamic collection, need to add/remove, order may change
NamedTuple
NamedTuple -- кортеж с именованными полями:
from typing import NamedTuple
# Modern syntax with class (preferred)
class Point(NamedTuple):
x: float
y: float
z: float = 0.0 # default value
p = Point(1.0, 2.0, 3.0)
print(p.x) # 1.0
print(p[0]) # 1.0 (still works like a tuple)
print(p) # Point(x=1.0, y=2.0, z=3.0)
# Unpacking works
x, y, z = p
print(f"({x}, {y}, {z})")
# Immutable
# p.x = 10 # AttributeError!
# _replace creates a new instance
p2 = p._replace(x=10.0)
print(p2) # Point(x=10.0, y=2.0, z=3.0)
# Convert to dict
print(p._asdict()) # {'x': 1.0, 'y': 2.0, 'z': 3.0}
# Practical example
class HTTPResponse(NamedTuple):
status: int
body: str
headers: dict[str, str] = {}
response = HTTPResponse(200, '{"ok": true}', {"Content-Type": "application/json"})
print(f"Status: {response.status}")
print(f"Body: {response.body}")
# Old syntax (still valid but less readable)
from collections import namedtuple
Color = namedtuple("Color", ["red", "green", "blue"])
red = Color(255, 0, 0)
Использование NamedTuple для возврата нескольких значений
from typing import NamedTuple
class Stats(NamedTuple):
mean: float
median: float
mode: float
std_dev: float
def calculate_stats(data: list[float]) -> Stats:
"""Calculate basic statistics for a dataset."""
import statistics
return Stats(
mean=statistics.mean(data),
median=statistics.median(data),
mode=statistics.mode(data),
std_dev=statistics.stdev(data),
)
data = [1, 2, 2, 3, 4, 4, 4, 5]
stats = calculate_stats(data)
print(f"Среднее: {stats.mean:.2f}")
print(f"Медиана: {stats.median}")
print(f"Мода: {stats.mode}")
print(f"СКО: {stats.std_dev:.2f}")
Полезные функции для последовательностей
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# Built-in functions
print(len(numbers)) # 8
print(min(numbers)) # 1
print(max(numbers)) # 9
print(sum(numbers)) # 31
print(sorted(numbers)) # [1, 1, 2, 3, 4, 5, 6, 9]
print(any(x > 8 for x in numbers)) # True
print(all(x > 0 for x in numbers)) # True
# Membership testing
print(5 in numbers) # True
print(10 in numbers) # False
# Enumerate for indexed iteration
for i, num in enumerate(numbers, start=1):
print(f"#{i}: {num}")
# Zip for parallel iteration
names = ["Иван", "Мария"]
ages = [25, 30]
for name, age in zip(names, ages):
print(f"{name}: {age}")
Итоги
- Список -- изменяемая последовательность, основная рабочая лошадка
- Кортеж -- неизменяемая последовательность, легче и быстрее
[[0]*3]*3-- ловушка с общими ссылками; используйте comprehensionlist.sort()сортирует на месте,sorted()создаёт новую последовательностьcopy.deepcopy()для полного копирования вложенных структур- NamedTuple -- кортежи с именованными полями и типами
- Кортежи можно использовать как ключи словарей (hashable)
- Распаковка
*позволяет гибко извлекать элементы