Сообщить об ошибке.

Список Python как аргумент по умолчанию

В разделе рассмотрен один из самых опасных случаев использование списка 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 не передается функции, что исправляет ошибку.