[Python-Dev] Oberver Pattern
Guido van Rossum
guido@python.org
Fri, 10 May 2002 18:16:59 -0400
[Raymond Hettinger]
> Changed to existing code are expected to be minimal.
> For a simple container type, implementation could be as small as
> changing the flag line and inserting one line, PyNotify(self).
[and later]
> Taking dictionaryobject for example, anywhere the code assigns
> a value to dp->me_key or dp->me_value, add a macro line:
>
> PyNotify(self);
>
> which expands to:
>
> if (self->being_observed) PyObject_Notify_All(self);
I think these two quoted sections contadict each other. If you have
to insert a call to PyNotify(self) everywhere the dict is modified,
you end up having to change a lot of places: every API that can change
the dict.
Note that you don't want to put the PyNotify() call in the
lowest-level insertdict() call, because then operations like update()
would notify the observer for each updated element, rather than once
for all updated elements.
It's worse for lists: there's a macro PyList_SET_ITEM(). You can't
change the macro to automatically call PyNotify(self), because (again)
that would notify the observer at too fine a granularity. But that
means you have to update all code that *uses* PyList_SET_ITEM(). And
that's a lot.
> When not being observed, the performance penalty is nearly zero.
Again, I think you are being just a dat naive here -- all those jumps
(that are nearly always taken!) add up, and you're proposing to add
this to every mutable object.
> When being observed, the cost is only a for loop over the function
> calls:
>
> for( i=0 ; i<numobservers ; i++)
> observerlist[i](self);
>
> The cost is only setting up the loop and making the call.
If you *have* to do this, I would implement (at the C level) just a
single observer object. If multiple observers are needed, this can
easily be done using an adapter in Python.
--Guido van Rossum (home page: http://www.python.org/~guido/)