Хорошие использует значения переменных аргумента переменной?
В 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)