Wie kann ich eine Merkmal der statischen Ereignisbenachrichtigung auf einer Liste abfeuern?
Frage
Ich arbeite durch die traits
Präsentation von Pycon 2010. Gegen 2:30:45 beginnt der Moderator abzudecken Benachrichtigungen über Merkmale, die unter anderem die Fähigkeit erlauben, ein Unterprogramm automatisch zu nennen, jederzeit a trait
hat sich verändert.
Ich führe eine modifizierte Kopie des Beispiels durch, das er gegeben hat ... In diesem Prozess versuche ich zu sehen, ob ich ein statisches Ereignis abfeuern kann, wenn ich eine Änderung vornehme volume
oder 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"
Wenn ich dieses Skript ausführe, kann ich sehen This one goes to eleven... so far, we have seen [5.0, 11.0]
in der Konsolenausgabe, also weiß ich das _volume_changed()
wird abgefeuert, wenn ich zuweise 11.0
zu spinal_tap.volume
.
Ich sehe jedoch nie Ereignisse von _inputs_changed()
. Egal welches Beispiel ich koche, ich kann keine bekommen List
ein Ereignis zu entlassen.
Dies ist die Ausgabe, die ich sehe ... Beachten Sie, dass es keine Beweise dafür gibt _inputs_changed()
jemals Feuer.
[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 ~]$
Ich habe dies sowohl unter python2.6 / cygwin / windows 7 als auch unter python 2.5 / linux (alle verwendet traits
Version 4.0.0, dass ich easy_install
direkt aus Auf der Seite von begeistert). Die Ergebnisse sind gleich, egal was ich bisher versucht habe.
Sollte a List
in der Lage sein, ein statisches Ereignis zu entlassen, wenn sie Eigenschaften verwenden? Wenn ja, mache ich etwas falsch?
Lösung
Nach dem Durchsuchen ihrer Unit -Tests fand ich einen Test für Dict
Merkmale bei begeisterten Event Unittest Berichterstattung... es sieht so aus, als wenn Sie einen Container wie ein haben Dict
oder List
Dass Sie die Magic Event -Listener -Methode wie folgt einrichten müssen:
## 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
Ebenso entdeckte ich auch, dass das on_trait_change
Dekorateur (verwendet für die Dynamik traits
Ereignisbenachrichtigung) erfordert eine ähnliche Nomenklatur, wenn Sie sie mit einem anrufen traits.api.List
oder traits.api.Dict
... damit ich den obigen Code auch schreiben konnte wie:
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
Wenn ich den Code ausführe, werde ich in jedem Fall die erwartete Ausgabe:
[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 ~]$
Andere Tipps
Da mich dies auch in letzter Zeit erwischt habe, habe ich die Antwort von Mike Pennington mit Merkmalen 4.2.1 überprüft. Es scheint eine Unterscheidung zwischen Änderungen des List -Merkmals selbst (z. B. der Zuweisung einer neuen Liste) und Änderungen an der Mitgliedschaft der Liste (z. B. Anhang oder Einstellung nach Index). Ersteres verwendet den gleichen Namen wie das Merkmal (z. B. zB inputs
), während letzteres das Suffix "_Items" verwendet. Dieses Beispiel scheint dies zu demonstrieren:
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>
Dieses Verhalten wird in der Dokumentation angedeutet hier.
Jedoch wenn an erweitert Name wird verwendet, es scheint möglich, die zu haben gleich Handler rief an, als die gesamte Liste oder Mitgliedschaft wird geändert:
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]
In diesem Fall die name
Parameter der update_costs
Der Handler kann verwendet werden, um zwischen dem änderenden Container selbst oder einem einzelnen Element innerhalb des Containers zu differenzieren.