В разделе рассмотрен один из самых опасных случаев использование списка list
(изменяемых последовательностей), когда список используется, как аргумент по умолчанию для функций.
Сморим следующий пример.
# определим функцию, где корзина покупателя 'basket' # по умолчанию должна быть пустым списком покупок def add_fruit(fruit, basket=[]): # при покупке - корзина пополняется basket.append(fruit) return basket >>> b = add_fruit("banana") >>> b # ['banana'] # Внимание! Аргумент `basket` не передается!!! >>> c = add_fruit("apple") >>> c # ['banana', 'apple'] # Да ладно, ведь при инициализации # в аргументах функции список пустой.
Из примера видно, что функция add_fruit()
вызывается дважды, при чем аргумент basket
(список сделанных покупок) не передается! Конечным результатом является список из двух товаров ['banana', 'apple']
, как это произошло?
Причина такого поведения заключается в том, что когда интерпретатор определяет функцию, он также создает аргумент по умолчанию. Затем он связывает этот аргумент и созданный объект (ставит ссылку на него в памяти). В примере, Python выделил пустой список и привязал его к аргументу basket
(корзине покупок).
Другими словами, пустой список создается один раз и аргумент basket
указывает на него в течение всего времени существования функции. Единственное исключение - это когда аргументу basket
передается другой список, но это не изменит значение по умолчанию. Всякий раз, когда вызывается функция снова, без указания basket
, то она будет использовать значение по умолчанию, которое было создано при определении функции.
Чтобы избежать подобного поведения, аргументы по умолчанию должны быть неизменяемыми! В данном конкретном случае, можно аргументу basket
присвоить значение None
и создать пустой список, если basket is None
, в противном случае работать со списком, который передается в функцию.
Смотрим:
def add_fruit(fruit, basket=None): if basket is None: basket = [] basket.append(fruit) return basket >>> b = add_fruit("banana") >>> b # ['banana'] >>> c = add_fruit("apple") >>> c # ['apple']
Теперь создается пустой список покупок всякий раз, когда аргумент basket
не передается функции, что исправляет ошибку.