Перейти к содержанию

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

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

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

Изменяемый аргумент по умолчанию (англ. mutable default argument) - распространенная, но легко решаемая проблема при работе с функциями и классами на Python. Такое решение является распространенным паттерном, и часто используется в программах на 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, то вместо него создается наше изменяемое значение.

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 по умолчанию, и присваивайте изменяемое значение в теле функции.