← Назад к вопросам

Как работает обращение к элементу списка по индексу?

2.2 Middle🔥 201 комментариев
#Python Core#Soft Skills

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает обращение к элементу списка по индексу

Обращение к элементу списка по индексу — это фундаментальная операция в Python. Под капотом происходит вызов специального метода __getitem__, который реализует логику индексирования.

1. Базовое индексирование

my_list = ['a', 'b', 'c', 'd', 'e']

# Положительные индексы (слева направо)
print(my_list[0])    # 'a'
print(my_list[2])    # 'c'
print(my_list[4])    # 'e'

# Отрицательные индексы (справа налево)
print(my_list[-1])   # 'e' (последний элемент)
print(my_list[-2])   # 'd' (предпоследний)
print(my_list[-5])   # 'a' (первый элемент)

Внутренняя работа:

Индекс:   0    1    2    3    4
        +----+----+----+----+----+
Элемент | a  | b  | c  | d  | e  |
        +----+----+----+----+----+
Отрицательные: -5  -4   -3   -2   -1

2. Волшебный метод __getitem__

Когда ты пишешь list[index], Python вызывает list.__getitem__(index):

# Эти два выражения равны
print(my_list[2])
print(my_list.__getitem__(2))

# Создаём свой класс с индексированием
class CustomList:
    def __init__(self, items):
        self.items = items
    
    def __getitem__(self, index):
        # Обработка положительных и отрицательных индексов
        if isinstance(index, int):
            # Преобразуем отрицательные индексы
            if index < 0:
                index = len(self.items) + index
            
            # Проверяем границы
            if index < 0 or index >= len(self.items):
                raise IndexError("list index out of range")
            
            return self.items[index]
        else:
            raise TypeError(f"indices must be integers, not {type(index).__name__}")

custom = CustomList(['x', 'y', 'z'])
print(custom[0])    # 'x'
print(custom[-1])   # 'z'
print(custom[1])    # 'y'

3. Срезы (Slicing)

Срезы также используют __getitem__, но с объектом slice:

my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Базовый срез
print(my_list[1:4])      # [1, 2, 3] (от индекса 1 до 3 включительно)
print(my_list[2:7:2])    # [2, 4, 6] (с шагом 2)
print(my_list[:5])       # [0, 1, 2, 3, 4] (от начала до 4)
print(my_list[5:])       # [5, 6, 7, 8, 9] (от 5 до конца)
print(my_list[::2])      # [0, 2, 4, 6, 8] (каждый 2-й элемент)
print(my_list[::-1])     # [9, 8, 7, ..., 0] (в обратном порядке)

# Под капотом это вызывает
print(my_list.__getitem__(slice(1, 4, None)))

# Реализация поддержки срезов в custom классе
class CustomList:
    def __init__(self, items):
        self.items = items
    
    def __getitem__(self, index):
        if isinstance(index, slice):
            # Обработка срезов
            start, stop, step = index.indices(len(self.items))
            return [self.items[i] for i in range(start, stop, step)]
        elif isinstance(index, int):
            # Обработка целого индекса
            if index < 0:
                index = len(self.items) + index
            if index < 0 or index >= len(self.items):
                raise IndexError("list index out of range")
            return self.items[index]

4. Сложные индексы

Многомерные списки:

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Доступ к элементу (строка 1, столбец 2)
print(matrix[1][2])      # 6

# Внутренно: matrix[1] возвращает [4, 5, 6], затем [2] возвращает 6

NumPy массивы:

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

# Можно использовать кортеж индексов
print(arr[0, 2])         # 3 (первая строка, третий столбец)
print(arr[1, :])         # [4, 5, 6] (вторая строка, все столбцы)
print(arr[:, 1])         # [2, 5] (все строки, второй столбец)

5. Проверка границ и обработка ошибок

my_list = [10, 20, 30]

# IndexError
try:
    print(my_list[10])  # Индекс вне диапазона
except IndexError as e:
    print(f"Error: {e}")  # "list index out of range"

# TypeError
try:
    print(my_list["invalid"])  # Неверный тип индекса
except TypeError as e:
    print(f"Error: {e}")  # "list indices must be integers or slices, not str"

# Безопасный доступ
index = 100
if 0 <= index < len(my_list):
    print(my_list[index])
else:
    print("Index out of range")

6. Производительность индексирования

# Список (list) — O(1) операция
my_list = list(range(1000000))
start = time.time()
for i in range(1000):
    _ = my_list[999999]  # Постоянное время
print(f"List access: {time.time() - start}")

# Связный список — O(n) операция
from collections import deque
my_deque = deque(range(1000000))
start = time.time()
for i in range(1000):
    _ = my_deque[-1]  # Линейное время для доступа по индексу
print(f"Deque access: {time.time() - start}")

# Словарь (dict) — в среднем O(1)
my_dict = {i: i*2 for i in range(1000000)}
start = time.time()
for i in range(1000):
    _ = my_dict[999999]
print(f"Dict access: {time.time() - start}")

7. Отрицательные индексы внутри

def safe_get(lst, index):
    """Безопасное получение элемента со всеми проверками"""
    n = len(lst)
    
    # Обработка отрицательных индексов
    if isinstance(index, int):
        if index < 0:
            actual_index = n + index
        else:
            actual_index = index
        
        if 0 <= actual_index < n:
            return lst[actual_index]
        else:
            raise IndexError("Index out of range")
    else:
        raise TypeError(f"Expected int, got {type(index).__name__}")

my_list = ['a', 'b', 'c']
print(safe_get(my_list, -1))    # 'c'
print(safe_get(my_list, 0))     # 'a'

8. Итераторы и индексирование

# Не все объекты поддерживают индексирование
my_tuple = (1, 2, 3)  # Поддерживает
my_set = {1, 2, 3}    # Не поддерживает
my_dict = {1: 'a'}    # Поддерживает (по ключам)

print(my_tuple[0])    # OK: 1
print(my_dict[1])     # OK: 'a'
print(my_set[0])      # Error: 'set' object is not subscriptable

# Проверка поддержки
from collections.abc import Sequence

print(isinstance(my_list, Sequence))  # True
print(isinstance(my_set, Sequence))   # False

Заключение

Обращение к элементу списка по индексу — это O(1) операция, которая напрямую вычисляет адрес в памяти. Python обрабатывает как положительные, так и отрицательные индексы, проверяет границы и вызывает метод __getitem__ под капотом. Эта простота скрывает мощный механизм, позволяющий реализовать кастомные структуры данных с собственной логикой индексирования.

Как работает обращение к элементу списка по индексу? | PrepBro