Хорошие использует значения переменных аргумента переменной?

В 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']

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

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

import random

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

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

Ответ 3

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

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

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

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

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

Ответ 5

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

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

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

Захват значений объектов в закрытии вместо переменной.

callbacks = []
for i in range(10):
    def callback(i=i): ...
    callbacks.append(callback)