ζ༼Ɵ͆ل͜Ɵ͆༽ᶘ

Удивительный случай изменяемых аргументов по умолчанию в Python

0 комментов
02.02.2022
2 мин чтения

Итак, у меня для вас небольшой сюрприз... 😀😀😀

У нас есть функция Python, которая принимает аргумент, которому также назначено значение по умолчанию.

def foo(a = []): 
    a.append(5) return a

Теперь, если я передам список функции, она добавит 5 нему...

>>> foo([1,2,3,4]) [1,2,3,4,5] 
>>> foo([1,2,3,4,5,6]) [1,2,3,4,5,6,5]

Но что происходит, когда мы не передаем никаких аргументов и предпочитаем использовать аргумент по умолчанию? Давайте посмотрим сами:

>>> foo() [5]
>>> foo() [5,5]
>>> foo() [5,5,5]

Что..?

Разве вывод не должен был представлять собой новый список 5, содержащий только один список элементов, который выглядел бы как [5]..?

Но это не то, что происходит на самом деле...

Почему так..?

Это потому, что наш аргумент по умолчанию является list изменяемым типом данных, т.е. его можно изменить, и это то, что здесь происходит.

На что следует обратить внимание:

Аргументы по умолчанию привязываются к функции, как только функция определена.

Таким образом, у нас есть один список, связанный с нашей функцией, а не новый список, создаваемый при каждом вызове функции. И один и тот же список модифицируется снова и снова. Наш код функции выше имитирует следующий код:

a = [] 
def foo(): 
    a.append(5) 
    return a

Здесь у нас есть один список, и функция всегда изменяет один и тот же список:

>>> foo() [5] 
>>> foo() [5,5] 
>>> foo() [5,5,5]

Итак, это случай с изменяемыми аргументами Python по умолчанию... Один такой объект создается при определении функции.

Какие доказательства..?

Давайте создадим еще одну функцию, которая будет выводить сообщение при выполнении и возвращать пустой список, а затем мы будем использовать ее для инициализации нашего аргумента по умолчанию.

>>> def eggs(): 
        print('eggs() executed') 
        return [] 
>>> def foo(a = eggs()): 
        a.append(5) 
        return a 

eggs() executed

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

Что происходит с неизменяемыми аргументами по умолчанию?

Они также обрабатываются таким же образом, т. е. как изменяемые аргументы по умолчанию, они также привязываются к функции сразу после окончания определения. Но, поскольку они неизменяемы, мы получаем те же результаты, а не измененные один раз. Давайте снова воспользуемся приведенным выше кодом:

>>> def eggs(): 
        print('eggs() executed') 
        return () # this time we use a tuple instead as these are immutable 
>>> def foo(a = eggs()): 
        a += (5,) 
        return a 

eggs() executed 
>>> foo() 
(5,) 
>>> foo() 
(5,)

Теперь вы видите, что они не меняются.

Вывод

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

Если вы хотите узнать больше, почему это было реализовано таким образом или в каких ситуациях вы могли оказаться, просто перейдите и прочитайте следующий ответ StackOverflow "Least Astonishment" and the Mutable Default Argument

3
Сегодня
День улёта