При использовании Python вы обнаружите, что используете my_list.pop() для удаления последнего элемента списка и my_dict.pop('key') для удаления элемента из словаря, и т.д.

Это может создать впечатление, что pop() - это универсальный метод экземпляра, который можно использовать с любым объектом. Так в чем же заключается суть этого метода pop()?


1. 'pop()' не единственный



Если сказать кратко, pop() для list и pop() для dict - это совершенно разные методы, хоть и имеют одинаковое название.

  • list класс определяет собственный метод pop(), подходящий для списков.

  • Класс dict также определяет свой собственный метод pop(), подходящий для словарей.

  • Класс set делает то же самое.

То, что разные объекты имеют методы с одинаковыми именами, связано с особенностью объектно-ориентированного программирования в Python, называемой полиморфизмом (Polymorphism). Название 'pop' подразумевает "извлечение элемента из структуры данных и его возврат" и имеет общепринятое значение.


2. Разные способы работы pop() в зависимости от класса

Однако наличие одинакового названия не означает, что их поведение будет одинаковым. Каждый класс реализует pop() согласно своей структуре данных.

1. list.pop([index])

  • Действие: удаляет элемент с указанным индексом из списка и возвращает его.

  • Особенность: Если индекс не указан, применяется значение по умолчанию -1, что приводит к удалению последнего элемента. Это аналогично операции 'pop' в структуре данных Stack.

  • Пример:

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

# Индекс не указан (удаление последнего элемента 'c')
last_item = my_list.pop()
print(f"Возвращенное значение: {last_item}, оставшийся список: {my_list}")
# Вывод: Возвращенное значение: c, оставшийся список: ['a', 'b']

# Указан индекс 0 ('a' удаляется)
first_item = my_list.pop(0)
print(f"Возвращенное значение: {first_item}, оставшийся список: {my_list}")
# Вывод: Возвращенное значение: a, оставшийся список: ['b']

2. dict.pop(key[, default])

  • Действие: удаляет элемент (ключ-значение) из словаря по указанному ключу и возвращает его значение.

  • Особенность: В отличие от list.pop(), необходимо передать ключ, а не индекс. Если ключа нет и передано значение default, возвращается это значение, если значение default не передано, возникает ошибка KeyError.

  • Пример:

my_dict = {'apple': 1, 'banana': 2}

# Удаление ключа 'apple'
item = my_dict.pop('apple')
print(f"Возвращенное значение: {item}, оставшийся словарь: {my_dict}")
# Вывод: Возвращенное значение: 1, оставшийся словарь: {'banana': 2}

# Попытка удалить несуществующий ключ ('grape') (возвращает значение по умолчанию 99)
default_item = my_dict.pop('grape', 99)
print(f"Возвращенное значение: {default_item}, оставшийся словарь: {my_dict}")
# Вывод: Возвращенное значение: 99, оставшийся словарь: {'banana': 2}

3. set.pop()

  • Действие: удаляет случайный элемент из множества (set) и возвращает его.

  • Особенность: Поскольку множество является неупорядоченной структурой данных, невозможно предсказать, какой элемент будет удален. pop() вызывается без передачи аргументов.

  • Пример:

my_set = {10, 20, 30}

# Удаление случайного элемента (неизвестно, какой будет результат)
item = my_set.pop()
print(f"Возвращенное значение: {item}, оставшееся множество: {my_set}")
# Возможный вывод 1: Возвращенное значение: 10, оставшееся множество: {20, 30}
# Возможный вывод 2: Возвращенное значение: 20, оставшееся множество: {10, 30}

3. Причина, по которой кажется, что он «повсюду»



Мы можем думать о pop() как о методе экземпляра. Это верно. pop() фактически является методом, принадлежащим каждому объекту (экземпляру).

Хотя это и выглядит как obj.pop(), pop() не является глобальной функцией или методом, определенным в родительском классе всех объектов. Когда Python вызывает obj.method(), он динамически проверяет, есть ли у объекта obj свойство (метод) с именем method.

  • my_list является экземпляром класса list, и в классе list определен метод pop.

  • my_dict является экземпляром класса dict, и в классе dict также определен метод pop.

Поэтому как my_list.pop(), так и my_dict.pop() работают.

Что произойдет, если попытаться вызвать метод pop() на объекте, который его не определяет? Как и ожидалось, возникнет ошибка AttributeError. Рассмотрим пример с кортежем.

# Кортеж (tuple) не может изменяться, поэтому у него нет метода pop().
my_tuple = (1, 2, 3)

# AttributeError: 'tuple' object has no attribute 'pop'
my_tuple.pop() 

Инфографика, иллюстрирующая метод pop() в Python в трех структурах данных

Резюме

  1. pop() не является глобальной функцией или общим методом Python.

  2. list, dict, set и т. д. - это методы, определенные отдельно для каждого контейнера.

  3. Причина, по которой они имеют одинаковое название, заключается в том, что они разделяют традиционное значение "извлечь и вернуть данные".

  4. Поскольку их поведение различно, list использует индексы, dict - ключи, а set - случайные элементы.