Как я могу отправить уведомление о статическом событии Traits в Список?
Я работаю через traits
презентация от PyCon 2010. Около 2:30:45 докладчик начинает освещать уведомления о событиях, которые позволяют (помимо прочего) автоматически вызывать подпрограмму в любое время. trait
изменился
Я запускаю измененную копию примера, который он дал... В этом испытании я пытаюсь выяснить, могу ли я запускать статическое событие всякий раз, когда я изменяю volume
или же inputs
,
from traits.api import HasTraits, Range, List, Float
import traits
class Amplifier(HasTraits):
"""
Define an Amplifier (a la Spinal Tap) with Enthought's traits. Use traits
to enforce values boundaries on the Amplifier's objects. Use events to
notify via the console when the volume trait is changed and when new volume
traits are added to inputs.
"""
volume = Range(value=5.0, trait=Float, low=0.0, high=11.0)
inputs = List(volume) # I want to fire a static trait event notification
# when another volume element is added
def __init__(self, volume=5.0):
super(Amplifier, self).__init__()
self.volume = volume
self.inputs.append(volume)
def _volume_changed(self, old, new):
# static event listener for self.volume
if not (new in self.inputs):
self.inputs.append(self.volume)
if new == 11.0:
print "This one goes to eleven... so far, we have seen", self.inputs
def _inputs_changed(self, old, new):
# static event listener for self.inputs
print "Check it out!!"
if __name__=='__main__':
spinal_tap = Amplifier()
spinal_tap.volume = 11.0
print "DIRECTLY adding a new volume input..."
spinal_tap.inputs.append(4.0)
try:
print "NEGATIVE Test... adding 12.0"
spinal_tap.inputs.append(12.0)
except traits.trait_errors.TraitError:
print "Test passed"
Когда я запускаю этот скрипт, я вижу This one goes to eleven... so far, we have seen [5.0, 11.0]
в выводе консоли, так что я знаю, что _volume_changed()
увольняют, когда я назначаю 11.0
в spinal_tap.volume
,
Тем не менее, я никогда не вижу событий от _inputs_changed()
, Независимо от того, какой пример я готовлю, я не могу получить List
провести событие.
Это вывод, который я вижу... обратите внимание, что нет никаких доказательств того, что _inputs_changed()
когда-либо пожары.
[mpenning@Bucksnort ~]$ python spinaltap.py
This one goes to eleven... so far, we have seen [5.0, 11.0]
DIRECTLY adding a new volume input...
NEGATIVE Test... adding 12.0
Test passed
[mpenning@Bucksnort ~]$
Я запустил это как под Python2.6 / Cygwin / Windows 7 и Python 2.5 / Linux (все с использованием traits
версия 4.0.0, которую я easy_install
прямо с сайта Enthought). Результаты одинаковы, независимо от того, что я пробовал до сих пор.
Должен ли List
быть в состоянии вызвать статическое событие при использовании черт? Если так, я делаю что-то не так?
2 ответа
Просмотрев их модульные тесты, я нашел тест для Dict
черты в освещении событий блока Enthought... это выглядит, когда у вас есть контейнер, как Dict
или же List
что вам нужно настроить метод прослушивания магических событий, например так:
## Broken method definition: def _inputs_changed(self, old, new):
# container event static listeners must be in the form of _foo_items_changed()
def _inputs_items_changed(self, old, new):
# static event listener for self.inputs
if len(new.added) > 0:
print "Check it out, we added %s to self.items" % new.added
elif len(new.removed) > 0:
print "Check it out, we removed %s from self.items" % new.removed
Кроме того, я также обнаружил, что on_trait_change
декоратор (используется для динамического traits
уведомление о событии) требует аналогичной номенклатуры, если вы вызываете его с traits.api.List
или же traits.api.Dict
... так что я мог бы также написать код выше, как:
from traits.api import on_trait_change
# ...
@on_trait_change('inputs_items')
def something_changed(self, name, new):
# static event listener for self.inputs
if len(new.added) > 0:
print "Check it out, we added %s to self.items" % new.added
elif len(new.removed) > 0:
print "Check it out, we removed %s from self.items" % new.removed
В любом случае, когда я запускаю код, я получаю ожидаемый результат:
[mpenning@Bucksnort ~]$ python spinaltap.py
Check it out, we added [5.0] to self.items
Check it out, we added [11.0] to self.items
This one goes to eleven... so far, we have seen [5.0, 11.0]
DIRECTLY adding a new volume input...
Check it out, we added [4.0] to self.items
NEGATIVE Test... adding 12.0
Test passed
[mpenning@Bucksnort ~]$
Так как это также недавно меня поразило, я только что подтвердил ответ Майка Пеннингтона с чертами 4.2.1. Кажется, существует различие между изменениями самого признака списка (такими как назначение ему нового списка) и изменениями в членстве списка (такими как добавление или настройка по индексу). Первый использует то же имя, что и черта (например, inputs
), тогда как последний использует суффикс "_items". Этот пример, кажется, демонстрирует это:
from traits.api import Float, HasTraits, Instance, List
class Part(HasTraits):
costs = List(Float)
# called when the actual List trait changes:
def _costs_changed(self, old, new):
print("Part::_costs_changed %s -> %s" % (str(old), str(new)))
# called when the contents of the List trait changes:
def _costs_items_changed(self, old, new):
print("Part::_costs_changed %s -> %s" % (str(old), str(new)))
class Widget(HasTraits):
part = Instance(Part)
def __init__(self):
self.part = Part()
self.part.on_trait_change(self.update_costs, 'costs')
self.part.on_trait_change(self.update_costs_items, 'costs_items')
def update_costs(self, name, new):
print("update_costs: %s = %s" % (name, str(new),))
def update_costs_items(self, name, new):
print("update_costs_items: %s = %s" % (name, str(new),))
w = Widget()
w.part.costs = [ 1.0, 2.0, 3.0 ]
# Part::_costs_changed [] -> [1.0, 2.0, 3.0]
# update_costs: costs = [1.0, 2.0, 3.0]
w.part.costs = [ 1.0, 2.0, 3.1 ]
# Part::_costs_changed [1.0, 2.0, 3.0] -> [1.0, 2.0, 3.1]
# update_costs: costs = [1.0, 2.0, 3.1]
w.part.costs[0] = 5.0
# Part::_costs_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007bd810>
# update_costs_items: costs_items = <traits.trait_handlers.TraitListEvent object at 0x1007bd810>
w.part.costs.append(4.0)
# Part::_costs_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007bd810>
# update_costs_items: costs_items = <traits.trait_handlers.TraitListEvent object at 0x1007bd810>
Это поведение указано в документации здесь.
Однако, если используется расширенное имя, кажется возможным вызвать тот же обработчик при изменении всего списка или членства:
from traits.api import Float, HasTraits, Instance, List
class Part(HasTraits):
costs = List(Float)
def _costs_changed(self, old, new):
print("_costs_changed %s -> %s" % (str(old), str(new)))
def _costs_items_changed(self, old, new):
print("_costs_items_changed %s -> %s" % (str(old), str(new)))
class Widget(HasTraits):
part = Instance(Part)
def __init__(self):
self.part = Part()
self.part.on_trait_change(self.update_costs, 'costs[]') # <-- extended name
def update_costs(self, name, new):
print("update_costs: %s = %s" % (name, str(new),))
w = Widget()
w.part.costs = [ 1.0, 2.0, 3.0 ]
# _costs_changed [] -> [1.0, 2.0, 3.0]
# update_costs: costs = [1.0, 2.0, 3.0]
w.part.costs = [ 1.0, 2.0, 3.1 ]
# _costs_changed [1.0, 2.0, 3.0] -> [1.0, 2.0, 3.1]
# update_costs: costs = [1.0, 2.0, 3.1]
w.part.costs[0] = 5.0
# _costs_items_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007c6f90>
# update_costs: costs_items = [5.0]
w.part.costs.append(4.0)
# _costs_items_changed <undefined> -> <traits.trait_handlers.TraitListEvent object at 0x1007c6f90>
# update_costs: costs_items = [4.0]
В этом случае name
параметр update_costs
Обработчик может использоваться для различения изменения самого контейнера или изменения одного элемента в контейнере.