ipywidget 观察方法每次更改都会调用几次,为什么?

ipywidget observe method called several times for every change, why?

我正在尝试了解 ipywidget 下拉菜单如何与观察方法一起工作,并遇到了这个非常有用的 SO 问题:

那里可以找到很好的解释。

稍微调整一下我创建这个小脚本的答案代码:

w = wd.Dropdown(
    options=['Addition', 'Multiplication', 'Subtraction'],
    value='Addition',
    description='Task:',
)

def on_change(change):
    print('method is called when printing this')
    if change['type'] == 'change' and change['name'] == 'value':
        print("changed to %s" % change.new)
    else:
        print('chage type is not change it is actually:', change['type'])
        print('chage name is not value it is actually:', change['name'])

w.observe(on_change)

display(w)

奇怪的是,当一次更改下拉菜单的值时,打印出来的是这样的:

method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: _property_lock
method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: label
method is called when printing this
changed to Multiplication
method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: index
method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: _property_lock

因此菜单下拉列表的一次更改调用了 4 次 observe 方法。

这是为什么?

其次,当 observe 写成这样时,这不会发生:

w.observe(on_change, names='value')

那么输出就是:

method is called when printing this
changed to Multiplication

所以在第二种情况下,该方法被调用一次。

谁能解释一下这是怎么回事?

w.observe(on_change)

你告诉小部件每次它的任何属性(我猜实际上是 Trait 属性)发生变化时调用 on_change 函数:这包括 value(这只是你大部分时间都需要)还有它的所有更多“内部属性”(比如 _property_lock,大多数时候你不需要)。 此行为记录在 w.observe 文档中:

Signature: w.observe(handler, names=traitlets.All, type='change')
Docstring:
Setup a handler to be called when a trait changes.

This is used to setup dynamic notifications of trait changes.

Parameters
----------
handler : callable
    A callable that is called when a trait changes. Its
    signature should be ``handler(change)``, where ``change`` is a
    dictionary. The change dictionary at least holds a 'type' key.
    * ``type``: the type of notification.
    Other keys may be passed depending on the value of 'type'. In the
    case where type is 'change', we also have the following keys:
    * ``owner`` : the HasTraits instance
    * ``old`` : the old value of the modified trait attribute
    * ``new`` : the new value of the modified trait attribute
    * ``name`` : the name of the modified trait attribute.
names : list, str, All
    If names is All, the handler will apply to all traits.  If a list
    of str, handler will apply to all names in the list.  If a
    str, the handler will apply just to that name.
type : str, All (default: 'change')
    The type of notification to filter by. If equal to All, then all
    notifications are passed to the observe handler.

names 默认为 traitlets.All,在这种情况下:If names is All, the handler will apply to all traits.

因此在定义回调时 names=Value 很重要。