Изменяемый аргумент по умолчанию Python

Изменяемый аргумент по умолчанию

Изменяемый аргумент по умолчанию (англ. mutable default argument) - распространенная, но легко решаемая проблема при работе с функциями и классами на Python.

В чем проблема?

Представьте себе, что мы написали функцию, которая добавляет к списку значение no_zeros, если список не содержит числа 0. Функция отлично работала, но затем мы решили, добавить пустой список по умолчанию:

>>> def no_zeros(lst=[]):
    if 0 not in lst:
        lst.append('no zeros')
    return lst
print(no_zeros([]))
print(no_zeros())
print(no_zeros([0, 1, 2]))
print(no_zeros())
print(no_zeros())
>>> print(no_zeros([]))
... print(no_zeros())
... print(no_zeros([0, 1, 2]))
... print(no_zeros())
... print(no_zeros())
['no zeros']
['no zeros']
[0, 1, 2]
['no zeros', 'no zeros']
['no zeros', 'no zeros', 'no zeros']

Пока мы передаем в функцию список, все работает отлично, но каждый раз, когда мы используем аргумент по умолчанию, в него добавляется 'no zeros'. А это значит, что мы имеем дело с изменяемым аргументом по умолчанию.

Что случилось?

Когда интерпретатор доходит до нашей функции он создает значение по умолчанию и использует его при вызове функции без соответствующего аргумента. Если это значение изменяется, то в дальнейшем оно используется уже в измененном виде. Неизменяемые типы (кортежи, строки и т.д.) защищены от таких действий, но списки, словари и другие изменяемые типы и классы могут создать проблемы.

Что делать?

Эта проблема решается множеством способов, но стандартным решением является следующее. Значением по умолчанию назначается None, а в теле функции пишется простая проверка. Если соответствующим аргументом выступает None, то вместо него создается наше изменяемое значение.

1
2
3
4
5
6
def no_zeros(lst=None):
    if lst is None:
        lst = []
    if 0 not in lst:
        lst.append(None)
    return lst
>>> print(no_zeros([]))
... print(no_zeros())
... print(no_zeros([0, 1, 2]))
... print(no_zeros())
... print(no_zeros())
[None]
[None]
[0, 1, 2]
[None]
[None]

В этом случае каждый раз создается новый список. Такое решение является распространенным паттерном, и часто используется в программах на Python, чтобы избежать изменяемых аргументов по умолчанию.

Заключение

Подводя итог, можно с уверенностью сказать, что не стоит использовать изменяемые аргументы по умолчанию без очень, очень веской причины. Вместо этого используйте значение None по умолчанию, и присваивайте изменяемое значение в теле функции.

Практический Python для начинающих
Практический Python для начинающих

Станьте junior Python программистом за 7 месяцев

 7 месяцев

Возможно будет интересно

🏆 Hello, world! Python
Новичок
🏆 Hello, world!

Мы вчера запустили новый www.pylot.me. Должны были в следующую среду, но запустили вчера.

2022-10-04
Как практиковаться в Python? Python
Новичок
Как практиковаться в Python?

Для улучшения качества знаний и повышения уровня программиста, необходим постоянный практикум. Где можно это организовать самостоятельно, и как практиковаться в Python?

2022-10-19
Условные конструкции и сопоставление структурных шаблонов Шпаргалки
Новичок
Условные конструкции и сопоставление структурных шаблонов

Шпаргалка по условным конструкциям и сопоставлению структурных шаблонов

2022-11-09