What is meant by 'avoid modifying the `objects` parameter directly.'?

In several pages in the docs, like here: Column — Panel v1.4.2

The below on objects is mentioned.

But what is meant exactly by ‘avoid modifying the objects parameter directly.’?

What is one suposed to avoid / not do? Can someone give an example of the ‘anti-pattern’?

The one exception is when replacing the list of objects entirely.

What is a code example of replacing the list entirely given the code example below?

Quote:

In general it is preferred to modify layouts only through the provided methods and avoid modifying the objects parameter directly.

The one exception is when replacing the list of objects entirely

w1 = pn.widgets.TextInput(name='Text:')
w2 = pn.widgets.FloatSlider(name='Slider')

column = pn.Column('# Column', w1, w2, styles=dict(background='WhiteSmoke'))
column

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.

I would add that of course going around the custom API would of course break the reactivity entirely. If you somehow bypassed the __setitem__ to modify things directly, you would end up with your stuff not displaying properly.

1 Like

Thank you @jerry.vinokurov for that detailed and informative reply! Greatly appeciated.:+1: