[Grok-dev] adding objects to context from __init__

Brandon Craig Rhodes brandon at rhodesmill.org
Sat Oct 4 18:03:27 EDT 2008

John de la Garza <john at jjdev.com> writes:

> In the tutorial the first demo of persistent objects did this:
> class Sample(grok.Application, grok.Container):
>      text = 'default text'

The difference you are seeing has do to with how Python works.  Roughly
speaking, when you ask an object for its "foo" attribute with something
like "print mysample.foo", it looks:

 - For a "foo" attribute on the "mysample" instance.
 - For a "foo" attribute on the class "Sample".
 - For a "foo" attribute on all the parent classes of "Sample".
 - For a "foo" attribute on the metaclass.

And Python returns the first value discovered.  So, when you say:

class Sample(grok.Application, grok.Container):
    text = 'default text'

you are creating a "safety" or "fall-through" value that will be
returned if an object is asked for its attribute "text" but does not
actually have one.  So:

>>> s = Sample()
>>> print s.text
'default text'
>>> s.text = 'not the default text'
>>> print s
'not the default text'

You see?  Until the "s" instance is given its own value for "text", it
uses the default one stored up in the class.

> Then as the tutorial goes on they do this:
> class Sample(grok.Application, grok.Container):
>      def __init__(self):
>          super(Sample, self).__init__()
>          self.list = []
> Why don't we have to add the string the same way as the list?  I mean
> the variable text gets persisted between restarts of the server, but
> if we added a list by doing this:
> class Sample(grok.Application, grok.Container):
>      list = []

I apologize if my using capital letters looks offensive, but I'm really
just trying to be very, very clear:

 - The variable "text" on the CLASS does NOT get persisted or stored
   ANYWHERE and is re-created each time you run your application.  So:

   class Sample(...):
       text = 'default text'

   does NOT store ANYTHING for "text" in the database no matter how many
   Sample objects you make.

 - The assignment to an object attribute DOES save a value to that
   INSTANCE in the database, without changing the definition your CODE
   gives of the class.  So:

   >>> s = Sample()
   >>> s.text = 'something else'

   DOES save the new value of "text" to the database when you finish the
   current transaction (in Zope, usually by completing the current web
   operation, but you can also: "import transaction; transaction.commit()").

 - This means that if you start up your application with new code that
   reads slightly differently:

   class Sample(...):
       text = 'different default text'

   that EVERY instance that never got ITS OWN value for "text" assigned
   will now start returning 'different default text' when you ask for
   its ".text" because, since it doesn't have ITS OWN value around in
   the database, it just shows its class's value instead.  But the
   instances that DO have their OWN value just keep returning it, and
   are not affected (unless you do a "del thissample.text" on them,
   which stops them from having their own ".text" any more and they WILL
   begin showing the class's value again).

 - To now answer your actual question: the reason that we CANNOT do the
   same trick with a list:

   class Sample(grok.Application, grok.Container):
       list = []   # BAD

   is that we do NOT alter lists by reassignment; that is, we do NOT say
   something like:

   >>> s = Sample()
   >>> s.list = [ 3,4,5 ]

   when we want to alter its list.  If we did, then all the above rules
   would work, and we would be fine.  But what we tend to do INSTEAD is:

   >>> s = Sample()
   >>> s.list.append(3)
   >>> s.list.append(4)

   which would NOT give "s" its OWN value for ".list", DIFFERENT than
   everyone else's value for it, but would make changes to the SHARED
   value of ".list" that's kept up in the class.

   Since the ".list" attribute will just keep being MODIFIED, but never
   REASSIGNED, we need to create a SEPARATE copy of the value on EVERY
   class member and CANNOT safely provide a single default kept up on
   the class instead of in the database.  That is why you are seeing:

   class Sample(grok.Application, grok.Container):
       def __init__(self):
           super(Sample, self).__init__()
           self.list = []

Again, sorry for all the caps; but an old German named Marshall Booth
used to use capital letters like this when explaining things to we young
people on the Mercedes mailing list, and it really helped.  So I thought
I would see if they would help here, just this once. :-)

Brandon Craig Rhodes   brandon at rhodesmill.org   http://rhodesmill.org/brandon

More information about the Grok-dev mailing list