1.. _on-trait-change-notification: 2 3=========================================== 4Trait Notification with **on_trait_change** 5=========================================== 6 7**on_trait_change** is an older method for setting up change notifications. It 8has several design flaws and limitations. A newer mechanism **observe** is 9introduced for overcoming them. See :ref:`observe-notification` for details and 10for how to migrate. 11 12Change notifications can be set up with **on_trait_change** in several ways: 13 14.. index:: notification; strategies 15 16* Dynamically, by calling on_trait_change() or on_trait_event() to establish 17 (or remove) change notification handlers. 18* Statically, by decorating methods on the class with the @on_trait_change 19 decorator to indicate that they handle notification for specified attributes. 20* Statically, by using a special naming convention for methods on the class to 21 indicate that they handle notifications for specific trait attributes. 22 23.. index:: notification; dynamic 24 25.. _dynamic-notification: 26 27Dynamic Notification 28-------------------- 29 30Dynamic notification is useful in cases where a notification handler cannot be 31defined on the class (or a subclass) whose trait attribute changes are to be 32monitored, or if you want to monitor changes on certain instances of a class, 33but not all of them. To use dynamic notification, you define a handler method 34or function, and then invoke the on_trait_change() or on_trait_event() method 35to register that handler with the object being monitored. Multiple handlers can 36be defined for the same object, or even for the same trait attribute on the 37same object. The handler registration methods have the following signatures: 38 39.. index:: on_trait_change; method 40 41.. method:: on_trait_change(handler[, name=None, remove=False, dispatch='same']) 42 43.. index:: on_trait_event(); method 44 45.. method:: on_trait_event(handler[, name=None, remove=False, dispatch='same']) 46 47In these signatures: 48 49* *handler*: Specifies the function or bound method to be called whenever the 50 trait attributes specified by the *name* parameter are modified. 51* *name*: Specifies trait attributes whose changes trigger the handler being 52 called. If this parameter is omitted or is None, the handler is called 53 whenever *any* trait attribute of the object is modified. The syntax 54 supported by this parameter is discussed in :ref:`the-name-parameter`. 55* *remove*: If True (or non-zero), then handler will no longer be called when 56 the specified trait attributes are modified. In other words, it causes the 57 handler to be "unhooked". 58* *dispatch*: String indicating the thread on which notifications must be run. 59 In most cases, it can be omitted. See the *Traits API Reference* for details 60 on non-default values. 61 62.. index:: examples; dynamic notification 63 64.. _example-of-a-dynamic-notification-handler: 65 66Example of a Dynamic Notification Handler 67````````````````````````````````````````` 68 69Setting up a dynamic trait attribute change notification handler is illustrated 70in the following example:: 71 72 # dynamic_notification.py --- Example of dynamic notification 73 from traits.api import Float, HasTraits, Instance 74 75 class Part (HasTraits): 76 cost = Float(0.0) 77 78 class Widget (HasTraits): 79 part1 = Instance(Part) 80 part2 = Instance(Part) 81 cost = Float(0.0) 82 83 def __init__(self): 84 self.part1 = Part() 85 self.part2 = Part() 86 self.part1.on_trait_change(self.update_cost, 'cost') 87 self.part2.on_trait_change(self.update_cost, 'cost') 88 89 def update_cost(self): 90 self.cost = self.part1.cost + self.part2.cost 91 92 # Example: 93 w = Widget() 94 w.part1.cost = 2.25 95 w.part2.cost = 5.31 96 print w.cost 97 # Result: 7.56 98 99In this example, the Widget constructor sets up a dynamic trait attribute 100change notification so that its update_cost() method is called whenever the 101**cost** attribute of either its **part1** or **part2** attribute is modified. 102This method then updates the cost attribute of the widget object. 103 104.. index:: name parameter; on_trait_change() 105 106.. _the-name-parameter: 107 108The *name* Parameter 109```````````````````` 110 111The *name* parameter of on_trait_change() and on_trait_event() provides 112significant flexibility in specifying the name or names of one or more trait 113attributes that the handler applies to. It supports syntax for specifying 114names of trait attributes not just directly on the current object, but also 115on sub-objects referenced by the current object. 116 117The *name* parameter can take any of the following values: 118 119* Omitted, None, or 'anytrait': The handler applies to any trait attribute on 120 the object. 121* A name or list of names: The handler applies to each trait attribute on the 122 object with the specified names. 123* An "extended" name or list of extended names: The handler applies to each 124 trait attribute that matches the specified extended names. 125 126.. index:: 127 pair: extended trait names; syntax 128 129.. _syntax: 130 131Syntax 132:::::: 133 134Extended names use the following syntax: 135 136.. productionList:: 137 xname: xname2['.'xname2]* 138 xname2: ( xname3 | '['xname3[','xname3]*']' ) ['*'] 139 xname3: xname | ['+'|'-'][name] | name['?' | ('+'|'-')[name]] 140 141A *name* is any valid Python attribute name. 142 143.. index:: 144 pair: extended trait names; semantics 145 146.. _semantics: 147 148Semantics 149::::::::: 150 151.. _semantics-of-extended-name-notation-table: 152 153.. rubric:: Semantics of extended name notation 154 155+------------------------------+----------------------------------------------+ 156| Pattern | Meaning | 157+==============================+==============================================+ 158|*item1*\ .\ *item2* |A trait named item1 contains an object (or | 159| |objects, if *item1* is a list or dictionary), | 160| |with a trait named *item2*. Changes to either | 161| |*item1* or *item2* trigger a notification. | 162+------------------------------+----------------------------------------------+ 163|*item1*\ :*item2* |A trait named **item1** contains an object (or| 164| |objects, if *item1* is a list or dictionary), | 165| |with a trait named *item2*. Changes to *item2*| 166| |trigger a notification, while changes to | 167| |*item1* do not (i.e., the ':' indicates that | 168| |changes to the link object are not reported. | 169+------------------------------+----------------------------------------------+ 170|[*item1*, *item2*, ..., |A list that matches any of the specified | 171|*itemN*] |items. Note that at the topmost level, the | 172| |surrounding square brackets are optional. | 173+------------------------------+----------------------------------------------+ 174|*item*\ [] |A trait named *item* is a list. Changes to | 175| |*item* or to its members triggers a | 176| |notification. | 177+------------------------------+----------------------------------------------+ 178|*name*? |If the current object does not have an | 179| |attribute called *name*, the reference can be | 180| |ignored. If the '?' character is omitted, the | 181| |current object must have a trait called | 182| |*name*; otherwise, an exception is raised. | 183+------------------------------+----------------------------------------------+ 184|*prefix*\ + |Matches any trait attribute on the object | 185| |whose name begins with *prefix*. | 186+------------------------------+----------------------------------------------+ 187|+\ *metadata_name* |Matches any trait on the object that has a | 188| |metadata attribute called *metadata_name*. | 189+------------------------------+----------------------------------------------+ 190|-*metadata_name* |Matches any trait on the current object that | 191| |does *not* have a metadata attribute called | 192| |*metadata_name*. | 193+------------------------------+----------------------------------------------+ 194|*prefix*\ +\ *metadata_name* |Matches any trait on the object whose name | 195| |begins with *prefix* and that has a metadata | 196| |attribute called *metadata_name*. | 197+------------------------------+----------------------------------------------+ 198|*prefix*\ -*metadata_name* |Matches any trait on the object whose name | 199| |begins with *prefix* and that does *not* have | 200| |a metadata attribute called *metadata_name*. | 201+------------------------------+----------------------------------------------+ 202|``+`` |Matches all traits on the object. | 203+------------------------------+----------------------------------------------+ 204|*pattern*\ * |Matches object graphs where *pattern* occurs | 205| |one or more times. This option is useful for | 206| |setting up listeners on recursive data | 207| |structures like trees or linked lists. | 208+------------------------------+----------------------------------------------+ 209 210.. index:: extended trait names; examples 211 212.. _examples-of-extended-name-notation-table: 213 214.. rubric:: Examples of extended name notation 215 216+--------------------------+--------------------------------------------------+ 217|Example | Meaning | 218+==========================+==================================================+ 219|``'foo, bar, baz'`` |Matches *object*.\ **foo**, *object*.\ **bar**, | 220| |and *object*.\ **baz**. | 221+--------------------------+--------------------------------------------------+ 222|``['foo', 'bar', 'baz']`` |Equivalent to ``'foo, bar, baz'``, but may be | 223| |useful in cases where the individual items are | 224| |computed. | 225+--------------------------+--------------------------------------------------+ 226|``'foo.bar.baz'`` |Matches *object*.\ **foo.bar.baz** | 227+--------------------------+--------------------------------------------------+ 228|``'foo.[bar,baz]'`` |Matches *object*.\ **foo.bar** and | 229| |*object*.\ **foo.baz** | 230+--------------------------+--------------------------------------------------+ 231|``'foo[]'`` |Matches a list trait on *object* named **foo**. | 232+--------------------------+--------------------------------------------------+ 233|``'([left,right]).name*'``|Matches the **name** trait of each tree node | 234| |object that is linked from the **left** or | 235| |**right** traits of a parent node, starting with | 236| |the current object as the root node. This pattern | 237| |also matches the **name** trait of the current | 238| |object, as the **left** and **right** modifiers | 239| |are optional. | 240+--------------------------+--------------------------------------------------+ 241|``'+dirty'`` |Matches any trait on the current object that has a| 242| |metadata attribute named **dirty** set. | 243+--------------------------+--------------------------------------------------+ 244|``'foo.+dirty'`` |Matches any trait on *object*.\ **foo** that has a| 245| |metadata attribute named **dirty** set. | 246+--------------------------+--------------------------------------------------+ 247|``'foo.[bar,-dirty]'`` |Matches *object*.\ **foo.bar** or any trait on | 248| |*object*.\ **foo** that does not have a metadata | 249| |attribute named **dirty** set. | 250+--------------------------+--------------------------------------------------+ 251 252For a pattern that references multiple objects, any of the intermediate 253(non-final) links can be traits of type Instance, List, or Dict. In the case of 254List or Dict traits, the subsequent portion of the pattern is applied to each 255item in the list or value in the dictionary. For example, if **self.children** 256is a list, a handler set for ``'children.name'`` listens for changes to the 257**name** trait for each item in the **self.children** list. 258 259.. note:: 260 In the case of Dict, List, and Set with nested patterns (e.g., 261 ``'children.name'``), not all handler signatures (see 262 :ref:`notification-handler-signatures`) are supported; see section 263 :ref:`dynamic-handler-special-cases` for more details. 264 265The handler routine is also invoked when items are added or removed from a list 266or dictionary, because this is treated as an implied change to the item's trait 267being monitored. 268 269.. index:: notification; dynamic 270 271.. _notification-handler-signatures: 272 273Notification Handler Signatures 274``````````````````````````````` 275 276The handler passed to on_trait_change() or on_trait_event() can have any one of 277the following signatures: 278 279.. index:: handler; signatures, trait change handler; signatures 280 281- handler() 282- handler(*new*) 283- handler(*name*, *new*) 284- handler(*object*, *name*, *new*) 285- handler(*object*, *name*, *old*, *new*) 286 287These signatures use the following parameters: 288 289.. index:: object parameter; notification handlers 290 291* *object*: The object whose trait attribute changed. 292 293.. index:: name parameter; notification handlers 294 295* *name*: The attribute that changed. If one of the objects in a sequence is a 296 List or Dict, and its membership changes, then this is the name of the trait 297 that references it, with '_items appended. For example, if the handler is 298 monitoring ``'foo.bar.baz'``, where **bar** is a List, and an item is added 299 to **bar**, then the value of the *name* parameter is 'bar_items'. 300 301.. index:: new parameter to the notification handlers 302 303* *new*: The new value of the trait attribute that changed. For changes to 304 List and Dict objects, this is a list of items that were added. 305 306.. index:: old parameter to the notification handlers 307 308* *old*: The old value of the trait attribute that changed. For changes to List 309 and Dict object, this is a list of items that were deleted. For event traits, 310 this is Undefined. 311 312If the handler is a bound method, it also implicitly has *self* as a first 313argument. 314 315.. index:: notification; special cases 316 317.. _dynamic-handler-special-cases: 318 319Dynamic Handler Special Cases 320````````````````````````````` 321 322In the one- and two-parameter signatures, the handler does not receive enough 323information to distinguish between a change to the final trait attribute being 324monitored, and a change to an intermediate object. In this case, the 325notification dispatcher attempts to map a change to an intermediate object to 326its effective change on the final trait attribute. This mapping is only 327possible if all the intermediate objects are single values (such as Instance or 328Any traits), and not List or Dict traits. If the change involves a List or 329Dict, then the notification dispatcher raises a TraitError when attempting to 330call a one- or two-parameter handler function, because it cannot unambiguously 331resolve the effective value for the final trait attribute. 332 333Zero-parameter signature handlers receive special treatment if the final trait 334attribute is a List or Dict, and if the string used for the *name* parameter is 335not just a simple trait name. In this case, the handler is automatically called 336when the membership of a final List or Dict trait is changed. This behavior can 337be useful in cases where the handler needs to know only that some aspect of the 338final trait has changed. For all other signatures, the handler function must be 339explicitly set for the *name*\ _items trait in order to called when the 340membership of the name trait changes. (Note that the *prefix*\ + and *item*\ [] 341syntaxes are both ways to specify both a trait name and its '_items' variant.) 342 343This behavior for zero-parameter handlers is not triggered for simple trait 344names, to preserve compatibility with code written for versions of Traits 345prior to 3.0. Earlier versions of Traits required handlers to be separately 346set for a trait and its items, which would result in redundant notifications 347under the Traits 3.0 behavior. Earlier versions also did not support the 348extended trait name syntax, accepting only simple trait names. Therefore, to 349use the "new style" behavior of zero-parameter handlers, be sure to include 350some aspect of the extended trait name syntax in the name specifier. 351 352.. index:: examples; handlers 353 354:: 355 356 # list_notifier.py -- Example of zero-parameter handlers for an object 357 # containing a list 358 from traits.api import HasTraits, List 359 360 class Employee: pass 361 362 class Department( HasTraits ): 363 employees = List(Employee) 364 365 def a_handler(): print("A handler") 366 def b_handler(): print("B handler") 367 def c_handler(): print("C handler") 368 369 fred = Employee() 370 mary = Employee() 371 donna = Employee() 372 373 dept = Department(employees=[fred, mary]) 374 375 # "Old style" name syntax 376 # a_handler is called only if the list is replaced: 377 dept.on_trait_change( a_handler, 'employees' ) 378 # b_handler is called if the membership of the list changes: 379 dept.on_trait_change( b_handler, 'employees_items') 380 381 # "New style" name syntax 382 # c_handler is called if 'employees' or its membership change: 383 dept.on_trait_change( c_handler, 'employees[]' ) 384 385 print("Changing list items") 386 dept.employees[1] = donna # Calls B and C 387 print("Replacing list") 388 dept.employees = [donna] # Calls A and C 389 390.. index:: notification; static 391 392.. _static-notification: 393 394Static Notification 395------------------- 396 397The static approach is the most convenient option, but it is not always 398possible. Writing a static change notification handler requires that, for a 399class whose trait attribute changes you are interested in, you write a method 400on that class (or a subclass). Therefore, you must know in advance what 401classes and attributes you want notification for, and you must be the author 402of those classes. Static notification also entails that every instance of the 403class has the same notification handlers. 404 405To indicate that a particular method is a static notification handler for a 406particular trait, you have two options: 407 408.. index:: 409 pair: decorator; on_trait_change 410 411* Apply the @on_trait_change decorator to the method. 412* Give the method a special name based on the name of the trait attribute it 413 "listens" to. 414 415.. _handler-decorator: 416 417Handler Decorator 418````````````````` 419The most flexible method of statically specifying that a method is a 420notification handler for a trait is to use the @on_trait_change() decorator. 421The @on_trait_change() decorator is more flexible than specially-named method 422handlers, because it supports the very powerful extended trait name syntax 423(see :ref:`the-name-parameter`). You can use the decorator to set handlers on 424multiple attributes at once, on trait attributes of linked objects, and on 425attributes that are selected based on trait metadata. 426 427.. index:: 428 pair: on_trait_change; syntax 429 430.. _decorator-syntax: 431 432Decorator Syntax 433:::::::::::::::: 434 435The syntax for the decorator is:: 436 437 @on_trait_change( 'extended_trait_name' ) 438 def any_method_name( self, ...): 439 ... 440 441In this case, *extended_trait_name* is a specifier for one or more trait 442attributes, using the syntax described in :ref:`the-name-parameter`. 443 444The signatures that are recognized for "decorated" handlers are the same as 445those for dynamic notification handlers, as described in 446:ref:`notification-handler-signatures`. That is, they can have an *object* 447parameter, because they can handle notifications for trait attributes that do 448not belong to the same object. 449 450.. index:: 451 pair: on_trait_change; semantics 452 453.. _decorator-semantics: 454 455Decorator Semantics 456::::::::::::::::::: 457 458The functionality provided by the @on_trait_change() decorator is identical to 459that of specially-named handlers, in that both result in a call to 460on_trait_change() to register the method as a notification handler. However, 461the two approaches differ in when the call is made. Specially-named handlers 462are registered at class construction time; decorated handlers are registered at 463instance creation time. 464 465By default, decorated handlers are registered prior to setting the object 466state. When an instance is constructed with a trait value that is different 467from the default, that is considered a change and will fire the associated 468change handlers. The ``post_init`` argument in @on_trait_change can be used 469to delay registering the handler to after the state is set. 470 471.. literalinclude:: /../../examples/tutorials/doc_examples/examples/post_init_notification.py 472 :start-after: post_init_notification 473 474.. index:: notification; specially-named handlers 475 476.. _specially-named-notification-handlers: 477 478Specially-named Notification Handlers 479````````````````````````````````````` 480 481There are two kinds of special method names that can be used for static trait 482attribute change notifications. One is attribute-specific, and the other 483applies to all trait attributes on a class. 484 485.. index:: _name_changed(), _name_fired() 486 487To notify about changes to a single trait attribute named name, define a method 488named _\ *name*\ _changed() or _\ *name*\ _fired(). The leading underscore 489indicates that attribute-specific notification handlers are normally part of a 490class's private API. Methods named _\ *name*\ _fired() are normally used with 491traits that are events, described in :ref:`trait-events`. 492 493To notify about changes to any trait attribute on a class, define a method 494named _anytrait_changed(). 495 496.. index:: 497 pair: examples; _anytrait_changed() 498 pair: static notification; examples 499 500Both of these types of static trait attribute notification methods are 501illustrated in the following example:: 502 503 # static_notification.py --- Example of static attribute 504 # notification 505 from traits.api import HasTraits, Float 506 507 class Person(HasTraits): 508 weight_kg = Float(0.0) 509 height_m = Float(1.0) 510 bmi = Float(0.0) 511 512 def _weight_kg_changed(self, old, new): 513 print('weight_kg changed from %s to %s ' % (old, new)) 514 if self.height_m != 0.0: 515 self.bmi = self.weight_kg / (self.height_m**2) 516 517 def _anytrait_changed(self, name, old, new): 518 print('The %s trait changed from %s to %s ' \ 519 % (name, old, new)) 520 """ 521 >>> bob = Person() 522 >>> bob.height_m = 1.75 523 The height_m trait changed from 1.0 to 1.75 524 >>> bob.weight_kg = 100.0 525 The weight_kg trait changed from 0.0 to 100.0 526 weight_kg changed from 0.0 to 100.0 527 The bmi trait changed from 0.0 to 32.6530612245 528 """ 529 530In this example, the attribute-specific notification function is 531_weight_kg_changed(), which is called only when the **weight_kg** attribute 532changes. The class-specific notification handler is _anytrait_changed(), and 533is called when **weight_kg**, **height_m**, or **bmi** changes. Thus, both 534handlers are called when the **weight_kg** attribute changes. Also, the 535_weight_kg_changed() function modifies the **bmi** attribute, which causes 536_anytrait_changed() to be called for that attribute. 537 538The arguments that are passed to the trait attribute change notification 539method depend on the method signature and on which type of static notification 540handler it is. 541 542.. note:: 543 The :func:`~.on_trait_change` and :func:`~.observe` decorators nullify 544 the effect of special naming. A method that looks like:: 545 546 @observe("foo") 547 def _foo_changed(self, event): 548 do_something_with(event) 549 550 will only be called once when ``foo`` changes, as a result of the 551 ``observe`` decorator. 552 553.. _attribute-specific-handler-signatures: 554 555Attribute-specific Handler Signatures 556````````````````````````````````````` 557 558For an attribute specific notification handler, the method signatures supported 559are: 560 561.. method:: _name_changed() 562.. method:: _name_changed(new) 563 :noindex: 564.. method:: _name_changed(old, new) 565 :noindex: 566.. method:: _name_changed(name, old, new) 567 :noindex: 568 569The method name can also be _\ *name*\ _fired(), with the same set of 570signatures. 571 572In these signatures: 573 574* *new* is the new value assigned to the trait attribute. 575* *old* is the old value assigned to the trait attribute. 576* *name* is the name of the trait attribute. The extended trait name syntax 577 is not supported. 578 579Note that these signatures follow a different pattern for argument 580interpretation from dynamic handlers and decorated static handlers. Both of 581the following methods define a handler for an object's **name** trait:: 582 583 def _name_changed( self, arg1, arg2, arg3): 584 pass 585 586 @on_trait_change('name') 587 def some_method( self, arg1, arg2, arg3): 588 pass 589 590However, the interpretation of arguments to these methods differs, as shown in 591the following table. 592 593.. _handler-argument-interpretation-table: 594 595.. rubric:: Handler argument interpretation 596 597======== =================== ================ 598Argument _\ *name*\ _changed @on_trait_change 599======== =================== ================ 600*arg1* *name* *object* 601*arg2* *old* *name* 602*arg3* *new* *new* 603======== =================== ================ 604 605.. _general-static-handler-signatures: 606 607General Static Handler Signatures 608````````````````````````````````` 609 610In the case of a non-attribute specific handler, the method signatures 611supported are: 612 613.. method:: _anytrait_changed() 614.. method:: _anytrait_changed(name) 615 :noindex: 616.. method:: _anytrait_changed(name, new) 617 :noindex: 618.. method:: _anytrait_changed(name, old, new) 619 :noindex: 620 621The meanings for *name*, *new*, and *old* are the same as for 622attribute-specific notification functions. 623 624.. _trait-events: 625 626Trait Events 627------------ 628.. index:: events 629 630The Traits package defines a special type of trait called an event. Events are 631instances of (subclasses of) the Event class. 632 633There are two major differences between a normal trait and an event: 634 635* All notification handlers associated with an event are called whenever any 636 value is assigned to the event. A normal trait attribute only calls its 637 associated notification handlers when the previous value of the attribute 638 is different from the new value being assigned to it. 639* An event does not use any storage, and in fact does not store the values 640 assigned to it. Any value assigned to an event is reported as the new value 641 to all associated notification handlers, and then immediately discarded. 642 Because events do not retain a value, the *old* argument to a notification 643 handler associated with an event is always the special Undefined object (see 644 :ref:`undefined-object`). Similarly, attempting to read the value of an event 645 results in a TraitError exception, because an event has no value. 646 647.. index:: 648 pair: events; examples 649 650As an example of an event, consider:: 651 652 # event.py --- Example of trait event 653 from traits.api import Event, HasTraits, List, Tuple 654 655 point_2d = Tuple(0, 0) 656 657 658 class Line2D(HasTraits): 659 points = List(point_2d) 660 line_color = RGBAColor('black') 661 updated = Event 662 663 def redraw(self): 664 pass # Not implemented for this example 665 666 def _points_changed(self): 667 self.updated = True 668 669 def _updated_fired(self): 670 self.redraw() 671 672In support of the use of events, the Traits package understands 673attribute-specific notification handlers with names of the form 674_\ *name*\ _fired(), with signatures identical to the _\ *name*\ _changed() functions. 675In fact, the Traits package does not check whether the trait attributes that 676_\ *name*\ _fired() handlers are applied to are actually events. The function 677names are simply synonyms for programmer convenience. 678 679Similarly, a function named on_trait_event() can be used as a synonym for 680on_trait_change() for dynamic notification. 681 682.. index:: Undefined object 683 684.. _undefined-object: 685 686Undefined Object 687```````````````` 688 689Python defines a special, singleton object called None. The Traits package 690introduces an additional special, singleton object called Undefined. 691 692The Undefined object is used to indicate that a trait attribute has not yet 693had a value set (i.e., its value is undefined). Undefined is used instead of 694None, because None is often used for other meanings, such as that the value 695is not used. In particular, when a trait attribute is first assigned a value 696and its associated trait notification handlers are called, Undefined is passed 697as the value of the old parameter to each handler, to indicate that the 698attribute previously had no value. Similarly, the value of a trait event is 699always Undefined. 700 701.. _trait-items-handlers: 702 703Container Items Events 704`````````````````````` 705.. index:: 706 pair: container items; event 707 single: _name_items_changed() 708 709For the container traits (List, Dict and Set) both static and dynamic handlers 710for the trait are only called when the entire value of the trait is replaced 711with another value; they do not get fired when the item itself is mutated 712in-place. To listen to internal changes, you need to either use a dynamic 713handler with the ``[]`` suffix as noted in the Table 714:ref:`semantics-of-extended-name-notation-table`, or you can define an 715*name*\ _items event handler. 716 717For these trait types, an auxiliary *name*\ _items Event trait is defined which 718you can listen to either with a static handler _\ *name*\ _items_changed() 719or a dynamic handler which matches *name*\ _items, and these handlers will be 720called with notifications of changes to the contents of the list, set or 721dictionary. 722 723.. index:: TraitListEvent, TraitSetEvent, TraitDictEvent 724 725For these handlers the *new* parameter is a :index:`TraitListEvent`, 726:index:`TraitSetEvent` or :index:`TraitDictEvent` object whose attributes 727indicate the nature of the change and, because they are Event handlers, the 728*old* parameter is Undefined. 729 730All of these event objects have **added** and **removed** attributes that 731hold a list, set or dictionary of the items that were added and removed, 732respectively. 733 734The TraitListEvent has an additional **index** attribute that holds either 735the index of the first item changed, or for changes involving slices with 736steps other than 1, **index** holds the _slice_ that was changed. For 737slice values you can always recover the actual values which were changed or 738removed via ``range(index.start, index.stop, index.end)``. 739 740The TraitDictEvent has an additional **changed** attribute which holds the 741keys that were modified and the _old_ values that those keys held. The new 742values can be queried from directly from the trait value, if needed. 743 744Handlers for these events should not mutate the attributes of the event 745objects, including avoiding in-place changes to **added**, **removed**, etc. 746 747.. _on-trait-change-dos-n-donts: 748 749 750Dos and Don’ts 751-------------- 752 753Don't assume handlers are called in a specific order 754```````````````````````````````````````````````````` 755 756Don't do this:: 757 758 @on_trait_change("name") 759 def update_number(self): 760 self.number += 1 761 762 @on_trait_change("name") 763 def update_orders(self): 764 if self.number > 5: 765 self.orders.clear() 766 767Do this instead:: 768 769 @on_trait_change("name") 770 def update(self): 771 number = self.number + 1 772 self.number = number 773 if number > 5: 774 self.orders.clear() 775 776The first example is problematic because when ``name`` changes, calling 777``update_orders`` after ``update_number`` produces a result that is different 778from calling ``update_number`` after ``update_orders``. 779 780Even if the change handlers appear to be called in a deterministic order, 781this would be due to implementation details that may not hold true across 782releases and platforms. 783 784Don't raise exception from a change handler 785``````````````````````````````````````````` 786 787Don't do this:: 788 789 name = String() 790 791 @on_trait_change("name") 792 def update_name(self, new): 793 if len(new) == 0: 794 raise ValueError("Name cannot be empty.") 795 796What to do instead depends on the use case. For the above use case, ``String`` 797supports length checking:: 798 799 name = String(minlen=1) 800 801Traits consider handlers for the same change event to be independent of each 802other. Therefore, any uncaught exception from one change handler will be captured 803and logged, so not to prevent other handlers to be called. 804