In https://github.com/holoviz/param/pull/826 a discussion started on how the watchers are specially handled when a Parameter is copied from class to an instance. I've come up with some examples that exhibit some bugs and interesting behavior (e.g. side effects of calling p.param.x).
Class-level watcher on default
store = []
def cb(event): store.append(event)
class P(param.Parameterized):
x = param.Parameter()
P.param.watch(cb, 'x')
P.x = 10
assert len(store) == 1
print(P.param.x.watchers)
assert 'value' in P.param.x.watchers
p = P()
print(P.param.x.watchers)
assert 'value' in P.param.x.watchers
p.param.x # side-effect
print(P.param.x.watchers)
assert P.param.x.watchers == {} # :( Watchers no longer saved on the class Parameter
print(p.param.x.watchers)
assert 'value' in p.param.x.watchers # :( Now saved on the instance Parameter
P.x = 20
p.x = 30
assert len(store) == 1 # Changes at the class or instance level no longer re-trigger the callback.
Class-level watcher on an Parameter attribute (constant)
store = []
def cb(event): store.append(event)
class P(param.Parameterized):
x = param.Parameter()
P.param.watch(cb, 'x', 'constant')
assert 'constant' in P.param.x.watchers
P.param.x.constant = True
assert len(store) == 1
p = P()
assert 'constant' in P.param.x.watchers
print(P.param.x.watchers)
p.param.x # side-effect
assert P.param.x.watchers == {} # :( Watchers no longer saved on the class Parameter
print(P.param.x.watchers)
print(p.param.x.watchers)
assert 'constant' in p.param.x.watchers # :( Now saved on the instance Parameter
p.param.x.constant = False
assert len(store) == 2 # Changing constant on the instance triggers the callback
P.param.x.constant is True # just checking the value
P.param.x.constant = False
assert len(store) == 2 # Changes at the class level no longer re-trigger the callback.
Instance-level watcher on value
class P(param.Parameterized):
x = param.Parameter()
l = param.Parameter([])
@param.depends('x', watch=True)
def cb(self):
self.l.append(self.x)
print(P.param.x.watchers)
assert P.param.x.watchers == {}
print(P.param._depends)
assert P.param._depends['watch'][0][3][0].what == 'value'
p = P()
print(P.param.x.watchers)
assert P.param.x.watchers == {}
print(p.param.watchers)
assert 'x' in p.param.watchers
print(P.param._depends)
assert P.param._depends['watch'][0][3][0].what == 'value'
p.x = 30
assert len(p.l) == 1
# Nothing wrong in this example, just checking how things work
Instance-level watcher on an attribute, two instances
class P(param.Parameterized):
x = param.Parameter()
l = param.List([])
@param.depends('x:constant', watch=True)
def cb(self):
print('cb', self.param.x.constant)
self.l.append(self.param.x.constant)
print(P.param.x.watchers)
assert P.param.x.watchers == {}
print(P.param._depends)
assert P.param._depends['watch'][0][3][0].what == 'constant'
p = P()
print(P.param.x.watchers)
assert 'constant' in P.param.x.watchers
p2 = P()
p2.param.x.constant = True
assert p2.l == [True] # ok
assert p.l == [False] # :( Changing the attribute on another instance triggered the callback attached to instance `p` too
Instance-level watcher on an attribute
class P(param.Parameterized):
x = param.Parameter()
l = param.List([])
@param.depends('x:constant', watch=True)
def cb(self):
self.l.append(self.param.x.constant)
print(P.param.x.watchers)
assert P.param.x.watchers == {}
print(P.param._depends)
assert P.param._depends['watch'][0][3][0].what == 'constant'
p = P()
print(P.param.x.watchers)
assert 'constant' in P.param.x.watchers # The watchers are temporarily set on the class Parameter?
P.param.x.constant = False
print(p.l, P.l)
assert p.l == P.l == []
p.param.x # side-effect
print(P.param.x.watchers)
assert P.param.x.watchers == {}
print(p.param.x.watchers)
assert 'constant' in p.param.x.watchers
p.param.x.constant is False # just checking the value
p.param.x.constant = True
print(p.l)
assert p.l == [True]
In https://github.com/holoviz/param/pull/826 a discussion started on how the watchers are specially handled when a Parameter is copied from class to an instance. I've come up with some examples that exhibit some bugs and interesting behavior (e.g. side effects of calling
p.param.x
).default
constant
)value