Отличный вопрос! Я опубликовал ответ в выпуске ipywidgets: https://github.com/jupyter-widgets/ipywidgets/issues/2296
Итак, вот как можно создать пользовательское представление с использованием представлений ipywidget, используя виджеты из поста. Ключ - это ссылка для привязки атрибутов к значениям виджетов.
import ipywidgets as widgets
from traitlets import link
from IPython.display import display
class FXWidgetView:
def __init__(self, model):
self.model = model
self.domestic_label = widgets.Label("Domestic quantity")
self.domestic_field = widgets.FloatText()
self.foreign_label = widgets.Label("Foreign quantity")
self.foreign_field = widgets.FloatText()
self.fx_label = widgets.Label("Exchange rate (domestic/foreign)")
self.fx_field = widgets.FloatText()
self.lock_label = widgets.Label("If rates change, keep ")
self.lock_field = widgets.Dropdown(options=["domestic", "foreign"])
self.lock_label_post = widgets.Label('fixed')
self.ipyview = widgets.HBox([widgets.VBox([self.domestic_label, self.foreign_label, self.fx_label, self.lock_label]),
widgets.VBox([self.domestic_field, self.foreign_field, self.fx_field, widgets.HBox([self.lock_field, self.lock_label_post])])])
link((model, 'domestic_qty'), (self.domestic_field, 'value'))
link((model, 'foreign_qty'), (self.foreign_field, 'value'))
link((model, 'fx_rate'), (self.fx_field, 'value'))
link((model, 'lock'), (self.lock_field, 'value'))
def _ipython_display_(self):
display(self.ipyview)
Я укажу, что Param какое-то время выступает за такое разделение, а @jbednar отмечает, что ipywidgets может реализовывать удобные функции для поддержки этого шаблона. Я думаю, что это хорошая идея - иметь несколько простых вспомогательных функций, которые являются шагом по сравнению с интерактивными функциями, которые берут класс HasTraits, анализируют его и предоставляют виджеты по умолчанию для различных признаков для общих случаев.
Идея позади этого пропуска состоит в том, что статические переменные только полезны в двух ситуациях: когда действительно необходимо использовать класс и когда действительно необходимо использовать генератор.
, Если Вы хотите присоединить информацию с сохранением информации к функции, в чем Вы нуждаетесь, класс. Тривиально простой класс, возможно, но класс, тем не менее:
def foo(bar):
static my_bar # doesn't work
if not my_bar:
my_bar = bar
do_stuff(my_bar)
foo(bar)
foo()
# -- becomes ->
class Foo(object):
def __init__(self, bar):
self.bar = bar
def __call__(self):
do_stuff(self.bar)
foo = Foo(bar)
foo()
foo()
, Если Вы хотите, чтобы поведение Вашей функции изменилось каждый раз, это называют, в чем Вы нуждаетесь, генератор:
def foo(bar):
static my_bar # doesn't work
if not my_bar:
my_bar = bar
my_bar = my_bar * 3 % 5
return my_bar
foo(bar)
foo()
# -- becomes ->
def foogen(bar):
my_bar = bar
while True:
my_bar = my_bar * 3 % 5
yield my_bar
foo = foogen(bar)
foo.next()
foo.next()
, Конечно, статические переменные полезны для быстрых-и-грязных сценариев, где Вы не хотите иметь дело со стычкой больших структур для небольших задач. Но там, Вам ничто действительно не нужно больше чем global
—, это может казаться a, но топорный, но это хорошо для маленьких, одноразовых сценариев:
def foo():
global bar
do_stuff(bar)
foo()
foo()
Одна альтернатива классу является функциональным атрибутом:
def foo(arg):
if not hasattr(foo, 'cache'):
foo.cache = get_data_dict()
return foo.cache[arg]
, В то время как класс является, вероятно, инструментом для очистки, эта техника может быть полезной и более хороша, по-моему, затем глобальное.
В Python 3 я использовал бы закрытие:
def makefoo():
x = 0
def foo():
nonlocal x
x += 1
return x
return foo
foo = makefoo()
print(foo())
print(foo())
Я думаю, что большая часть использования локальных статических переменных должна моделировать генераторы, то есть, имея некоторую функцию, которая выполняет некоторое повторение процесса, возвращает результат, но поддерживает состояние для последующего вызова. Python обрабатывает это очень изящно использование эти yield
команда, таким образом, кажется, что нет такого большого количества потребности в статических переменных.
Это - проектное решение.
я предполагаю, что Guido думает, что Вам не нужны они очень часто, и Вам никогда действительно не нужны они: можно всегда просто использовать глобальную переменную и говорить всем держать их жирные лапы offa' переменная;-)
Для кэширования или memoization цели, декораторы могут использоваться в качестве изящного и общего решения.
Ответ в значительной степени то же как, почему никто не использует статические методы (даже при том, что они существуют). У Вас есть пространство имен уровня модуля, которое служит о той же цели, как класс был бы так или иначе.
Опрометчивая альтернатива:
можно также использовать побочные эффекты оценки времени определения функциональных значений по умолчанию:
def func(initial=0, my_static=[])
if not my_static:
my_static.append(initial)
my_static[0] += 1
return my_static[0]
print func(0), func(0), func(0)
действительно ужасный и легко ниспровергавший, но работы. Используя global
было бы более чистым, чем это, imo.
Из одного из Ваших комментариев: "Я хотел бы использовать их для вещей кэша, загруженных из диска. Я думаю, что это создает помехи экземпляру меньше, если я мог бы присвоить их функции"
Использование кэширующийся класс тогда, поскольку класс или экземпляр приписывают Вашему другому классу. Тем путем можно использовать полный набор функций классов, не создавая помехи другим вещам. Кроме того, Вы получаете допускающий повторное использование инструмент.
Это показывает, что на ТАК всегда окупается для утверждения проблемы вместо того, чтобы просить определенное низкоуровневое решение (например, недостающую функцию языка). Тот путь, вместо бесконечных дебатов о моделировании "статичного" (устаревшая функция с древнего языка, по моему мнению) кто-то, возможно, дал хороший ответ Вам проблема раньше.