2013-05-17 1 views
3

Новые пользователи python часто сбрасываются с помощью изменяемых аргументов по умолчанию. Каковы проблемы с запуском и другие проблемы использования этой функции, например, чтобы получить правильные настройки по умолчанию во время выполнения, которые продолжают правильно отображаться в сигнатурах функций через help()?Использует однократную привязку аргументов функции к плохой идее?

class MutableString (str): 

    def __init__ (self, value): 
     self.value = value 

    def __str__ (self): 
     return self.value 

    def __repr__ (self): 
     return "'" + self.value + "'" 


defaultAnimal = MutableString('elephant') 

def getAnimal (species=defaultAnimal): 
    'Return the given animal, or the mutable default.' 
    return species 

И в использовании:

>>> help(getAnimal) 
getAnimal(species='elephant') 
    Return the given animal, or the mutable default. 
>>> print getAnimal() 
elephant 
>>> defaultAnimal.value = 'kangaroo' 
>>> help(getAnimal) 
getAnimal(species='kangaroo') 
    Return the given animal, or the mutable default. 
>>> print getAnimal() 
kangaroo 
+2

Не делайте этого. Он также работает над опытными программистами. –

+1

Вы читали [Почему значения по умолчанию, разделяемые между объектами?] (Http://docs.python.org/3.3/faq/design.html#why-are-default-values-shared-between-objects) в официальном ВОПРОСЫ-ОТВЕТЫ? (Он не отвечает на весь ваш вопрос, но вы обязательно должны его прочитать в первую очередь.) – abarnert

+0

Я прочитал его и многое другое. Я понимаю, как значения по умолчанию привязаны во время определения, и поэтому они ссылаются на них. То, о чем я прошу, зная, как это работает, и пытается использовать его, и задается вопросом о непредвиденных ошибках (специфики, а не «не делайте этого!»). Я лично не вижу проблемы с вышеуказанным, так как это просто изменение значения по умолчанию, используемого при вызове функции. –

ответ

4

Во-первых, читайте Why are default values shared between objects. Это не отвечает на ваш вопрос, но это дает некоторый фон.


Существует различное допустимые варианты использования этой функции, но они в значительной степени все разделяют нечто общее: значение по умолчанию является прозрачным, простым, очевидно, изменчивым, встроенным типом. Запоминающие тайники, аккумуляторы для рекурсивных вызовов, необязательные выходные переменные и т. Д. Все выглядят так. Таким образом, опытные разработчики Python обычно обнаруживают один из этих вариантов использования - если они видят memocache={} или accum=[], они будут знать, чего ожидать. Но ваш код не будет похож на использование изменчивых значений по умолчанию, что будет столь же обманчиво для экспертов, как и для новичков.


Другая проблема заключается в том, что ваша функция выглядит как он возвращает строку, но она лжет:

>>> print getAnimal() 
kangaroo 
>>> print getAnimal()[0] 
e 

Конечно проблема здесь заключается в том, что вы реализовали MutableString неправильно, не то, что это невозможно реализовать ... но все же это должно показать, почему попытка «обмануть» интерпретатора, и ваши пользователи стремятся открыть дверь для неожиданных ошибок.

-

Очевидный способ справиться с этим, чтобы сохранить изменяющийся по умолчанию в атрибуте экземпляра модуля, функции, или (если это метод), а также использовать None в качестве значения по умолчанию. Или, если None является допустимым значением, какой-то другой страж:

defaultAnimal = 'elephant' 
def getAnimal (species=None): 
    if species is None: 
     return defaultAnimal 
    return species 

Заметьте, что это в значительной степени именно то, что говорит FAQ. Даже если у вас есть изменчивое значение, вы должны сделать этот танец, чтобы обойти проблему. Таким образом, вы определенно не должны создавать изменяемое значение из неотъемлемо неизменяемого до , создавая проблему.

Да, это означает, что help(getAnimal) не показывает текущие значения по умолчанию. Но никто этого не ожидает.

Они, вероятно, будет ожидать от вас, чтобы сказать им, что значение по умолчанию является крюком, конечно, но это работа для строки документации:

defaultAnimal = 'elephant' 
def getAnimal (species=None): 
    """getAnimal([species]) -> species 

    If the optional species parameter is left off, a default animal will be 
    returned. Normally this is 'elephant', but this can be configured by setting 
    foo.defaultAnimal to another value. 
    """ 
    if species is None: 
     return defaultAnimal 
    return species 
+1

«прозрачный, простой, очевидно-изменчивый» = пятно на :) –

+0

«Очевидно, изменчивость» - это действительно большое соображение и то, что я надеялся раскрыть с моим вопросом. Мой пример неочевиден, хотя меня больше интересовали варианты использования в методах класса. Например, рассмотрим 'Grid.create (spacing = [20,40])'. Я мог бы оставить эту опцию '__init__' и предоставить метод' setSpacing', и не позволять передавать ее 'create' или скрывать ее в' ** kwargs', но она казалась «прозрачной, простой и очевидной» для разрешите его установку, а затем посмотрите, какие текущие значения находятся в 'help (myGrid.create)'. Я всегда мог обойти его с помощью метода просмотра свойств. –

+0

Также замечательный момент о неожиданных ошибках и ожиданиях разработчиков, но я небрежно восставал против нескольких вещей здесь. Например, я не считаю, что «arg = None» - хорошая вещь; он требует, чтобы код чтения вместо сигнатур функций понимал, что на самом деле довольно простые вещи. –

2

Единственным полезным использование я видел на нем в качестве кэш-памяти:

def fibo(n, cache={}): 
    if n < 2: 
     return 1 
    else: 
     if n in cache: 
      return cache[n] 
     else: 
      fibo_n = fibo(n-1) + fibo(n-2) # you can still hit maximum recursion depth 
      cache[n] = fibo_n 
      return fibo_n 

... но тогда это уборщик использовать @lru_cache декоратор.

@lru_cache 
def fibo(n): 
    if n < 2: 
     return 1 
    else: 
     return fibo(n-1) + fibo(n-2) 
+1

Это не использование _only_. Например, это очевидный способ передать накопитель для рекурсии вниз. Кроме того, он прямо не отвечает на его вопрос. Но это определенно полезная иллюстрация. – abarnert