Хорошо ли использовать значения по умолчанию для изменяемого аргумента функции? - PullRequest
51 голосов
/ 06 февраля 2012

Распространенной ошибкой в ​​Python является установка изменяемого объекта в качестве значения по умолчанию для аргумента в функции. Вот пример, взятый из этой превосходной статьи Дэвида Гуджера :

>>> def bad_append(new_item, a_list=[]):
        a_list.append(new_item)
        return a_list
>>> print bad_append('one')
['one']
>>> print bad_append('two')
['one', 'two']

Объяснение, почему это происходит, здесь .

А теперь мой вопрос: Есть ли хороший вариант использования для этого синтаксиса?

Я имею в виду, если каждый, кто сталкивается с ней, совершает одну и ту же ошибку, отлаживает ее, понимает проблему и оттуда пытается ее избежать, какая польза от такого синтаксиса?

Ответы [ 5 ]

37 голосов
/ 06 февраля 2012

Вы можете использовать его для кэширования значений между вызовами функций:

def get_from_cache(name, cache={}):
    if name in cache: return cache[name]
    cache[name] = result = expensive_calculation()
    return result

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

6 голосов
/ 06 февраля 2012
import random

def ten_random_numbers(rng=random):
    return [rng.random() for i in xrange(10)]

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

5 голосов
/ 06 февраля 2012

Возможно, вы не изменяете изменяемый аргумент, но ожидаете изменяемого аргумента:

def foo(x, y, config={}):
    my_config = {'debug': True, 'verbose': False}
    my_config.update(config)
    return bar(x, my_config) + baz(y, my_config)

(Да, я знаю, что вы можете использовать config=() в данном конкретном случае, но я считаю, что менее понятными менее общий.)

4 голосов
/ 14 июля 2017

Канонический ответ на этой странице: http://effbot.org/zone/default-values.htm

В нем также упоминаются 3 "хороших" варианта использования для изменяемого аргумента по умолчанию:

  • привязка локальной переменной к текущее значение внешней переменной в обратном вызове
  • кэш / запоминание
  • локальная привязка глобальных имен (для высоко оптимизированного кода)
1 голос
/ 06 февраля 2012

РЕДАКТИРОВАТЬ (уточнение): проблема изменяемого аргумента по умолчанию является признаком более глубокого выбора проекта, а именно, что значения аргумента по умолчанию сохраняются в качестве атрибутов объекта функции.Вы можете спросить, почему был сделан этот выбор;как всегда, на такие вопросы сложно ответить правильно.Но он, безусловно, имеет хорошее применение:

Оптимизация для производительности:

def foo(sin=math.sin): ...

Получение значений объекта в замыкании вместо переменной.

callbacks = []
for i in range(10):
    def callback(i=i): ...
    callbacks.append(callback)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...