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!"