[Zope] ZClasses vs. Python Products

Paul Winkler pw_lists@slinkp.com
Sat, 4 May 2002 11:57:57 -0400


On Sat, May 04, 2002 at 01:09:44AM -0700, John Schinnerer wrote:
> Aloha,
> 
> I have also gotten the impression, from mail archives etc. etc., that
> python is the overall better way to create products.  So far I've done
> one simple Catalog-aware product with ZClasses and am about to try a
> simple python product.  Thanks for the link on auto-updating
> attributes, etc. - I didn't know that wouldn't happen with python
> products.

You didn't ask for this, but I'm going to take the opportunity
to write a bried essay on the topic of object persistence in
python. Helps me get it clearer in my own mind. :)

It all has to do with the way python resolves names.
Let's take a simple class as an example:

>>> class Foo:
...    def draw(self): print "I'm drawing at", x, y
...
>>> f = Foo()
>>> f.x = 10
>>> f.y = 20
>>> f.draw()
I'm drawing at 10 20

OK so far. but where does python get the values of x, y, and the
code for draw()? 

What python does when you say "foo.bar" is this:
1) does foo have bar in its local namespace?
   If so, use it.
   If not:
2) does the class (of which foo is an instance) have bar in its namespace?
   If so, use it.
   If not:
3) does the first superclass of this class have bar in its instance?
   If so, use it.
   If not... keep going up the chain of superclasses until you find
   it...

In the case of methods, they don't live in the object's
own namespace; they'll be found in the namespace of the
instantiated class or a superclass. This makes sense
because objects of the same class want to share the
code for their methods, rather than having copies in 
each instance. (This is different than C++ where each
object really *does* get a copy of the code, AFAIK).

So when python tries to resolve f.draw(), it finds
no draw in f and goes up to the class f was instantiated
from, and finds it there.

Data members will have different values for each object,
or else what's the point of having more than one instance?
So when python tries to resolve f.x, it immediately finds
it in f's namespace and uses that.

So what happens if, in the example above, I now do this:

>>> class Foo():
...    def draw(self):  print "new method drawing at", x, y
...
>>> f.draw()
I'm drawing at 10 20

Huh??? What happened here? Didn't I just tell you that methods
are looked up in the class namespace? Why is f still drawing
the old way?

Well, names are actually just references to memory locations.
And it turns out that when you redefine Foo, the new class code
lives at a new memory location. The object f still has a reference
to its parent class's location. The old code won't be removed
from memory as long as there is at least one object (f) that
has a reference to it.

But now, if we make f persistent, e.g. by using pickle, 
the object is saved to disk with the *name* of its instantiating
class, not the memory location. After all, if you run your
program more than once, it's highly unlikely that any of the
memory locations are going to be the same.

So if you re-load the pickled object, what actually happens
is that you get a *new* object of the same class, and its
data members will be assigned the old values. But method code
will be looked up in the current memory location of a class
with the right name, so the methods could have changed completely
since you saved the object, and the new methods will be used.

Let's see an example, in the same python session as before:

>>> f.draw()
I'm drawing at 10 20
>>> import pickle
>>> outfile = open("test.dump", "w")
>>> pickle.dump(f, outfile)

We've now saved the f object to the file "test.dump".
Now let's reload it from there:

>>> infile = open("test.dump", "r")
>>> f = pickle.load(myfile)
>>> f.draw()
New method at 10 20

See? f.x and f.y have their old values, 
and f.draw() is getting the new code from the new Foo.

Now substitute the ZODB for pickle, and you have some idea of
what's going on in Zope.

-- 

Paul Winkler
home:  http://www.slinkp.com
"Muppet Labs, where the future is made - today!"