У нас есть возможность задавать аргументам значение по умолчанию,
но эта инициализация происходит только один раз в момент компиляции
в байт-код, и далее они обновляться не будут, каждый новый вызов
функции будет сохранять эти аргументы по умолчанию, они будут
сохранены в атрибуте function.__defaults__ этот атрибут содержит
кортеж с аргументами по умолчанию.
function.__defaults__ - атрибут содержит кортеж с аргументами
функции по умолчанию.
Если аргументами по умолчанию являются неизменяемые типы, то все в порядке, от вызова функции к вызову функции они меняться не будут, но в случае если это изменяемый тип, то тут может возникнуть проблема, потому что тут от вызова к вызову эти аргументы по умолчанию будут меняться.
Рассмотрим такой пример кода, тут по умолчанию есть множество, от вызова к вызову это множество меняется, потому что множество изменяемый тип, так что тут надо быть аккуратным.
def unique(iterable, seen=set()):
acc = []
for item in iterable:
if item not in seen:
seen.add(item)
acc.append(item)
return acc
xs = [1, 1, 2, 3]
print(unique(xs))
print('Аргументы по умолчанию', unique.__defaults__)
xs = [1, 1, 2, 3, 4, 5, 6, 7]
print(unique(xs))
print('Аргументы по умолчанию', unique.__defaults__)
# Вывод
# [1, 2, 3]
# Аргументы по умолчанию ({1, 2, 3},)
# [4, 5, 6, 7]
# Аргументы по умолчанию ({1, 2, 3, 4, 5, 6, 7},)Вывод: стоит избегать в качестве аргументов по умолчанию изменяемые типы данных.
Как тогда поступить ? Поступать стоит так, в качестве аргумента по умолчанию все таки использовать неизменяемый тип данных, а после в самой функции ее инициализировать, к примеру можно сделать так:
def unique(iterable, seen=None):
acc = []
seen = set(seen or [])
for item in iterable:
if item not in seen:
seen.add(item)
acc.append(item)
return accЭта строчка seen = set(seen or []) задает множество, либо то
что было передано, либо полностью пустое, то есть None интерпретируется
как false мы получаем пустое множество, а если множество было передано
то оно и вставляется.