You are (in theory) not supposed to do column.objects[index] = item
. The reason for this is that objects
follows a descriptor pattern. This means that when you assign to it, what it’s doing is intercepting that assignment through the __set__
method and modifying the class to which it belongs. This pattern allows you to trigger all sorts of downstream changes that need to be triggered when the objects list is replaced. That said, for the ListLike
class, which is what objects
is, it does have a custom __setitem__
as well as all sort of other common list methods that you would use to modify a list, such as append
, pop
, insert
, etc. So I’m not sure to what degree this matters for most layouts. I do think it matters for certain layouts like Tabs
because I’ve noticed issues with names not updating properly in certain cases, but for Column
and Row
I think you can do whatever you want.
I think the real reason why the pattern of modifying container objects
this way is discouraged is because it is expected that you will do the updates in the __panel__
method. Since __panel__
itself is not a method you should be connecting any watchers to (it’s called automatically by Panel itself), it’s expected that __panel__
will render the layout from scratch whenever it’s invoked. That said, my usual pattern is to have __panel__
draw the initial container and then use other methods that react to user input or other state changes to modify that container. I have not noticed (excepting caveats above) that either changing a layout directly or assigning to it a whole other list makes much of a difference; it’s more a question to me of whether you do need to recompute the entire list or whether you are modifying some specific item. To illustrate what I mean, consider the following code:
import panel as pn
class Foo(pn.viewable.Viewer):
text = pn.widgets.TextInput(name='enter text')
def __init__(self, **params):
super().__init__(**params)
self.entries = pn.Column()
@pn.depends('text.value', watch=True)
def on_update(self):
self.entries.append(pn.widgets.StaticText(value=self.text.value))
def __panel__(self):
return pn.Column(self.text, self.entries)
f = Foo()
f.servable()
Every time you type into the text box and hit Enter, a new item will be appended. But this is also valid:
class Foo(pn.viewable.Viewer):
text = pn.widgets.TextInput(name='enter text')
def __init__(self, **params):
super().__init__(**params)
self.entries = pn.Column(objects=['default'])
@pn.depends('text.value', watch=True)
def on_update(self):
self.entries[0] = self.text.value
def __panel__(self):
return pn.Column(self.text, self.entries)
If you do this, changing the text will result in the text being updated (not appended of course). But as you might expect, this requires that you know a priori how to determine the index of the thing you are updating. In many cases it is just easier to recompute the whole list of objects from scratch than it is to track indices, but really that depends on your particular use case. My code has plenty of examples of both and which method I use is largely context-dependent.