Share This
Связаться со мной
Крути в низ
Categories
//List astonishment in Python

List astonishment in Python

07.10.2020Category : Python

Автор: PythonInDepth

Допустим, мы хотим написать функцию, которая будет принимать на вход список. Естественным ходом выглядит написать что-то вроде:

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

Ждем, что при каждом новом вызове функции список снова будет пустым. На самом же деле:

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

Вот это поворот! Причина такого поведения в том, что в Python значения дефолтных аргументов вычисляются только один раз — при объявлении функции. То есть, после того, как инструкция def выполнена, список data уже создан. При этом пока функция существует, пересоздаваться список больше не будет. Это объясняет и вот такое поведение:

>>> foo(data=[1, 2, 3]) [1, 2, 3, 5] >>> foo() [5, 5, 5]

Видно, что, когда мы передали другой список в foo(), к нему добавилось значение 5. При этом аргумент по умолчанию не изменился.

Как с этим жить? Либо вообще не использовать изменяемые объекты в качестве дефолтных значний, либо инициализировать их как None:

>>> def bar(data=None): ...     if data is None: ...         data = [] ...     data.append(5)   ...     return data

Такое определение создает новый список в локальном неймспейсе. Теперь все чисто.

>>> bar() [5] >>> bar() [5] >>> bar(data=[1, 2, 3]) [1, 2, 3, 5]

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

def baz(arg1, arg2, _cache={}):     # Если есть в кеше, выходим     if (arg1, arg2) in _cache:         return _cache[(arg1, arg2)]      # Вычисляем результат     result = ...      # Сохраняем в кеш     _cache[(arg1, arg2)] = result                return result

Сохранение результатов для предотвращения повторных вычислений называется мемоизацией.

В Python дефолтные аргументы инициализируются только при объявлении функции. Это значит, что изменения, которые делаются в мутабельных аргументах при вызове функции, сохраняются. Поэтому лучше не использовать списки, словари и экземпляры самописных классов как значения по умолчанию. Либо использовать специально, как кеш, в котором можно хранить полезную информацию.

Закончу цитатой:

«Не повторяйте одну и ту же ошибку несколько раз. Напишите функцию с этой ошибкой и вызывайте, когда будет нужно».

    Проходите тест по Python и поймите, готовы ли вы идти на курсы
  • 5 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

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

Связаться со мной
Close