2013-10-06 1 views
1

При написании кода на Python я часто нахожусь в поиске поведения, похожего на defvar Lisp. В принципе, если какая-то переменная не существует, я хочу ее создать и присвоить ей определенное значение. В противном случае я не хочу ничего делать, и, в частности, я не хочу переопределять текущее значение переменной.Аналог девара в Python

Я посмотрел вокруг онлайн и нашел это предложение:

try: 
    some_variable 
except NameError: 
    some_variable = some_expensive_computation() 

Я его помощью, и он прекрасно работает. Однако для меня это выглядит как код, который не является парадигматически правильным. Код - это четыре строки, а не 1, которые потребуются в Lisp, и требует обработки исключений, чтобы справиться с чем-то, что не является «исключительным».

Контекст в том, что я делаю интерактивное развитие. Я часто выполняю мой код кода Python, когда я его улучшаю, и я не хочу запускать some_expensive_comput() каждый раз, когда я это делаю. Я мог бы организовать запуск some_expensive_comput() вручную каждый раз, когда начинаю новый интерпретатор Python, но я бы скорее сделал что-то автоматическое, особенно для того, чтобы мой код можно было запустить неинтерактивно. Как бы программа Python в сезоне достигла этого?

Я использую WinXP с SP3, Python 2.7.5 через Anaconda 1.6.2 (32-разрядный) и работаю внутри Spyder.

+1

PINL (Python Is LISP) ... исключения используются для управления потоком в Python (см., Например, 'StopIteration'). – kindall

+0

, если вы делаете это в контексте класса, который вы можете сделать, если hasattr (self, 'some_variable') ', но, честно говоря, использование исключений для управления потоком прекрасное, как говорит @kindall. – roippi

+0

Для потомков: каждый, кто использует Spyder и сохраняющий переменные во время выполнения файла Python, должен быть осторожным с [UMD Spyder] (http://stackoverflow.com/questions/13876306/spyder-umd-has-deleted-module). Запуск «import bs4» разбил все существующие объекты bs4.BeautifulSoup (давая одну из нескольких ошибок, конечная причина которых заключалась в поиске). Решение заключалось в том, чтобы посетить Tools -> Preferences -> Console -> Advanced Settings и добавить bs4 в список исключенных модулей. – kuzzooroo

ответ

1

Вместо того, чтобы писать Lisp в Python, просто подумайте о том, что вы пытаетесь сделать. Вы хотите избежать двойного вызова дорогостоящей функции и запустить ее два раза. Вы можете написать функцию сделать, что:

def f(x): 
    if x in cache: 
     return cache[x] 

    result = ... 
    cache[x] = result 

    return result 

Или использовать декоратор Python и просто украсить функцию с другой функцией, которая заботится о кэшировании для вас. Python 3.3 поставляется с functools.lru_cache, который делает это:

import functools 

@functools.lru_cache() 
def f(x): 
    return ... 

Есть немало запоминания библиотек в PyPi для 2.7.

+0

Я думаю, что здесь возникает вопрос о том, как создать переменную кеша без его сброса при каждом выполнении моего файла. У меня не может быть строки, которая говорит «cache = {}», если только она не будет защищена от работы, если эта переменная уже существует. Тем не менее я беру вашу точку зрения на воспоминания. Некоторые или все из этих библиотек, вероятно, имеют встроенные смартфоны, чтобы они не перезагружали свои кеши. – kuzzooroo

+0

@kuzzooroo: Я никогда не говорил, что 'cache' был словарем. Вы можете использовать модуль 'shelve' и хранить все на диске. – Blender

+0

Использование полки (предназначено для настойчивости) - это аккуратное предложение. Знаете ли вы, можно ли снова и снова открывать один и тот же файл в виде полки (каждый раз, когда я запускаю свой код)? В попытке этого я считаю, что у меня есть несколько полки одного и того же файла одновременно, и вызов gc.collect, похоже, не заставит их уйти. – kuzzooroo

2

Как правило, это плохая идея полагаться на существование или нет переменной, имеющей смысл. Вместо этого используйте значение дозорного, чтобы указать, что переменная не установлена ​​в соответствующее значение. None - общий выбор для такого рода дозорных, хотя это может быть нецелесообразно, если это возможный вывод ваших дорогостоящих вычислений.

Таким образом, вместо текущего кода, сделать что-то вроде этого:

# early on in the program 
some_variable = None 

# later: 
if some_variable is None: 
    some_variable = some_expensive_computation() 

# use some_variable here 

Или, версия, где None может быть значимой величиной:

_sentinel = object() 
some_variable = _sentinel # this means it doesn't have a meaningful value 

# later 
if some_variable is _sentinel: 
    some_variable = some_expensive_computation() 
+0

Проблема, с которой я борюсь, заключается в том, что, не делая ничего умного, «some_variable = None» будет запускаться каждый раз при выполнении буфера. – kuzzooroo

+0

Я не уверен, что есть хороший способ справиться с постоянной средой, когда вы захотите повторно запустить свой код при его редактировании. Возможно, вы могли бы что-то сделать с 'reload' (' imp.reload' в Python 3), но это было бы очень хаки. Возможной альтернативой может быть сохранение ваших дорогостоящих вычислений данных где-то вне рабочего кода, например, в файле pickle. – Blckknght

1

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

Но я согласен с тем, что это не хорошая реализация концепции «здесь значение по умолчанию, используйте его, если переменная уже установлена». Python напрямую не поддерживает это для переменных, но у него есть по умолчанию-сеттера для ключей словаря:

myvalues = dict() 
myvalues.setdefault("some_variable", 42) 
print some_variable # prints 42 

Первый аргумент setdefault должен стать строка, содержащая имя переменной, которая будет определена.

Если у вас была сложная система настроек и значений по умолчанию (например, emacs), вы, вероятно, сохраните системные настройки в своем словаре, так что это все, что вам нужно. В вашем случае, вы можете также использовать setdefault непосредственно на глобальных переменных (только), с помощью встроенной функции globals() которая возвращает изменяемый словарь:

globals().setdefault("some_variable", 42) 

Но я бы рекомендовал использовать словарь для ваши постоянные переменные (вы можете использовать метод try... except для его создания условно). Он держит вещи в чистоте, и кажется, что это еще ... pythonic.

+0

«Более реалистичным решением будет использование словаря для вашего хранилища ключей, а не глобального пространства имен, тогда' defvar' проверит там и будет действовать соответствующим образом ». Как бы вы посоветовали мне не переустанавливать словарь каждый раз, когда я запускаю свой файл кода? Техника 'try ... except'? – kuzzooroo

+0

Да, я бы сделал это для самого словаря (так только один раз); то вы можете использовать стандартный 'dict' метод' setdefault', который делает именно то, что вы хотите (а также возвращает правильное значение). Если вы хотите использовать 'dict', это определенно способ пойти ... Хм, я просто понял, что вы можете использовать' setdefault' в 'globals()' тоже! – alexis

2

Трудно сказать, что вас больше беспокоит, особенности языка или постоянный сеанс. Поскольку вы говорите:

Контекст в том, что я делаю интерактивное развитие. Я часто выполняю мой код кода Python, когда я его улучшаю, и я не хочу запускать some_expensive_comput() каждый раз, когда я это делаю.

Вы можете обнаружить, что IPython обеспечивает постоянную интерактивную среду, которая вам нравится.

0

Позвольте мне попытаться суммировать то, что я узнал здесь:

  1. Использование обработку исключений для управления потоком хорошо в Python. Я мог сделать это один раз, чтобы настроить дикт, в котором я могу хранить то, что захочу.
  2. Существуют библиотеки и языковые функции, предназначенные для некоторой настойчивости; они могут обеспечить «дорожные» решения для некоторых приложений. Модуль полки является очевидным кандидатом здесь, но я бы толкнул «некоторую форму настойчивости» достаточно широко, чтобы включить @ Blender предложить использовать memoization.