2012-02-06 1 views
28

Общей ошибкой является установка изменяемого объекта в качестве значения по умолчанию аргумента в функции. Вот пример, взятый из this excellent write-up by David Goodger:Хорошо использует переменные значения аргументов функции?

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

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

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

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

+0

См Http: // stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – sdolan

+1

Лучшее объяснение, которое я знаю для этого, связано с связанным вопросом: функции являются первоклассными объектами, как и классы. Классы имеют изменяемые данные атрибутов; функции имеют изменяемые значения по умолчанию. – katrielalex

+0

http: // stackoverflow.com/questions/2639915/why-the-mutable-default-argument-fix-syntax-is-so-ugly-asks-python-newbie – katrielalex

ответ

21

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

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

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

+5

... или памятный декоратор. –

+9

'@ functools.lru_cache (maxsize = None)' – katrielalex

+2

@katrielalex lru_cache является новым в Python 3.2, поэтому не каждый может его использовать. – Duncan

4
import random 

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

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

+4

Но это тоже не очень важный прецедент. –

1

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

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

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

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

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

целые числа и встроенные функции не изменяются! – WolframH

+2

@Jonathan: В остальном примере по-прежнему нет изменчивого аргумента по умолчанию, или я его просто не вижу? – WolframH

+1

@ Джонатан: Моя точка зрения заключается не в том, что они изменяемы. Это то, что система Python использует для хранения аргументов по умолчанию - на объекте функции, определенном во время компиляции, может быть полезна. Это подразумевает изменение переменной аргумента по умолчанию, поскольку повторная оценка аргумента при каждом вызове функции сделает трюк бесполезным. – katrielalex

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=() в данном конкретном случае, но я считаю, что менее ясно . и менее общие)

1

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

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

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

Хотя эта ссылка может ответить на вопрос, лучше включить здесь основные части ответа и предоставить ссылку для справки. Ответные ссылки могут стать недействительными, если связанная страница изменится. - [Из обзора] (/ review/low-quality-posts/18212166) – Dijkgraaf