使用Bokeh的MultiSelect组件时触发PropertyValueList错误的解决问询
关于Bokeh MultiSelect回调报错的问题分析与解决
错误原因
你碰到的这个TypeError其实很好理解:MultiSelect的value属性返回的是一个列表(具体是Bokeh的PropertyValueList,本质是列表子类),而不是单个字符串。当你取消注释那行更新选项的代码后,new变量对应的是用户选中的所有选项组成的列表(比如你测试时的['oranges']),这时候你写new in x相当于尝试判断「一个列表是否包含在字符串里」,这完全不符合Pythonin运算符的规则——in左边如果是要检查的元素,右边得是可迭代对象;反过来左边是列表、右边是字符串的话,Python就会抛出你看到的这个错误。
举个直观的例子,你当前的错误代码相当于在执行['oranges'] in 'oranges',这显然是不合法的,自然会触发类型错误。
修复方案
核心思路就是先把new这个列表里的字符串元素取出来,再去做包含判断。下面分两种常见场景给出修复代码:
场景1:支持多选,筛选包含任意选中项的选项
如果你的MultiSelect允许多选,想要保留所有包含任意一个选中项的选项,可以用any()来实现:
from bokeh.io import curdoc from bokeh.layouts import widgetbox from bokeh.models import MultiSelect df = ["apples", "oranges", "grapes"] def callback(attr, old, new): print(df) print("{} changed: Old [ {} ] -> New [ {} ]".format(attr, old, new)) # 筛选包含任意一个选中项的选项 filtered_options = [x for x in df if any(term in x for term in new)] multiselect1.update(options=filtered_options) multiselect1 = MultiSelect(title="multiselect1", name="multiselect1", value=[], options=df) multiselect1.on_change('value', callback) curdoc().add_root(widgetbox(multiselect1))
场景2:强制单选,只基于第一个选中项筛选
如果你希望限制用户只能选一个选项,可以先设置max_selections=1,然后直接取new的第一个元素来判断(记得先判断new不为空,避免取消选中时的索引错误):
from bokeh.io import curdoc from bokeh.layouts import widgetbox from bokeh.models import MultiSelect df = ["apples", "oranges", "grapes"] def callback(attr, old, new): print(df) print("{} changed: Old [ {} ] -> New [ {} ]".format(attr, old, new)) # 先判断new不为空,再取第一个元素做筛选 if new: filtered_options = [x for x in df if new[0] in x] multiselect1.update(options=filtered_options) multiselect1 = MultiSelect(title="multiselect1", name="multiselect1", value=[], options=df, max_selections=1) multiselect1.on_change('value', callback) curdoc().add_root(widgetbox(multiselect1))
额外注意点
- 一定要判断
new是否为空列表:当用户取消所有选中项时,new会变成空列表,如果直接取new[0]会触发IndexError,所以必须加判断。 PropertyValueList和普通列表几乎没有区别:你可以像操作普通Python列表一样遍历、索引它,不需要额外的特殊处理。
内容的提问来源于stack exchange,提问作者flamenco
相关产品推荐
相关产品推荐

