Commit 9ea0214a authored by Jason Madden's avatar Jason Madden

Incorporate feedback.

- Move doctest example into comment.
- Merge the (advanced) sections.
- Typos.
parent bce996ca
...@@ -517,15 +517,17 @@ Things you can do, but need to carefully consider (advanced) ...@@ -517,15 +517,17 @@ Things you can do, but need to carefully consider (advanced)
While you can do anything with a persistent subclass that you can with While you can do anything with a persistent subclass that you can with
a normal subclass, certain things have additional implications for a normal subclass, certain things have additional implications for
persistent objects. These often show up as performance issues. persistent objects. These often show up as performance issues, or the
result may become hard to maintain.
Implement ``__eq__`` and ``__hash__`` Implement ``__eq__`` and ``__hash__``
------------------------------------- -------------------------------------
When you store an entry into a Python ``dict`` (or the persistent When you store an entry into a Python ``dict`` (or the persistent
variant ``PersistentMapping``) the key's ``__eq__`` and variant ``PersistentMapping``, or a ``set`` or ``frozenset``), the
``__hash__`` methods are used to determine where to store the value. key's ``__eq__`` and ``__hash__`` methods are used to determine where
Later they are used to look it up via ``in`` or ``__getitem__``. to store the value. Later they are used to look it up via ``in`` or
``__getitem__``.
When that ``dict`` is later loaded from the database, the internal When that ``dict`` is later loaded from the database, the internal
storage is rebuild from scratch. This means that every key has its storage is rebuild from scratch. This means that every key has its
...@@ -536,12 +538,12 @@ By default, every object, including persistent objects, inherits an ...@@ -536,12 +538,12 @@ By default, every object, including persistent objects, inherits an
implementation of ``__eq__`` and ``__hash__`` from :class:`object`. implementation of ``__eq__`` and ``__hash__`` from :class:`object`.
These default implementations are based on the object's *identity*, These default implementations are based on the object's *identity*,
that is, its unique identifier within the current Python process. that is, its unique identifier within the current Python process.
Calling, them, therefore is very fast, even on ghosts, and doesn't Calling, them, therefore is very fast, even on :ref:`ghosts
cause a ghost to load its state. <ghost-label>`, and doesn't cause a ghost to load its state.
If you override ``__eq__`` and ``__hash__`` in a custom persistent If you override ``__eq__`` and ``__hash__`` in a custom persistent
subclass, however, when you use that instances of that class as a key subclass, however, when you use instances of that class as a key
in a ``dict``, then the instance will have to be a unghosted before it in a ``dict``, then the instance will have to be unghosted before it
can be put in the dictionary. If you're building a large dictionary can be put in the dictionary. If you're building a large dictionary
with many such keys that are ghosts, you may find that loading all the with many such keys that are ghosts, you may find that loading all the
object states takes a considerable amount of time. If you were to object states takes a considerable amount of time. If you were to
...@@ -583,8 +585,12 @@ inherits identity-based ``__eq__`` and ``__hash__``:: ...@@ -583,8 +585,12 @@ inherits identity-based ``__eq__`` and ``__hash__``::
def add_author(self, author): def add_author(self, author):
self.authors += (author, ) self.authors += (author, )
Lets see an example of how these classes behave when stored in a .. -> src
dictionary. First, lets store some dictionaries::
>>> exec(src)
Lets see an example of how these classes behave when stored in a
dictionary. First, lets store some dictionaries::
>>> import ZODB >>> import ZODB
...@@ -594,8 +600,8 @@ dictionary. First, lets store some dictionaries:: ...@@ -594,8 +600,8 @@ dictionary. First, lets store some dictionaries::
>>> conn1.root.with_ident = {Book(str(i)) for i in range(5000)} >>> conn1.root.with_ident = {Book(str(i)) for i in range(5000)}
>>> transaction.commit() >>> transaction.commit()
Now, in a new connection (so we don't have any objects cached), lets Now, in a new connection (so we don't have any objects cached), lets
load the dictionaries:: load the dictionaries::
>>> conn2 = db.open() >>> conn2 = db.open()
>>> all((book._p_status == 'ghost' for book in conn2.root.with_ident)) >>> all((book._p_status == 'ghost' for book in conn2.root.with_ident))
...@@ -604,10 +610,13 @@ load the dictionaries:: ...@@ -604,10 +610,13 @@ load the dictionaries::
False False
We can see that all the objects that did have a custom ``__eq__`` We can see that all the objects that did have a custom ``__eq__``
and ``__hash__`` were loaded into memory, while those that did weren't. and ``__hash__`` were loaded into memory, while those that did weren't.
There are some alternatives:
There are two possible solutions: - Avoiding the use of persistent objects as keys in dictionaries or
entries in sets sidesteps the issue.
- If your application can tolerate identity based comparisons, simply - If your application can tolerate identity based comparisons, simply
don't implement the two methods. This means that objects will be don't implement the two methods. This means that objects will be
...@@ -620,21 +629,21 @@ There are two possible solutions: ...@@ -620,21 +629,21 @@ There are two possible solutions:
instances of those classes as keys. instances of those classes as keys.
- Make your classes `orderable - Make your classes `orderable
<https://pythonhosted.org/BTrees/#total-ordering-and-persistence>`_ and <https://pythonhosted.org/BTrees/#total-ordering-and-persistence>`_
use them as keys in a BTree instead of a dictionary. Even though and use them as keys in a BTree or entries in a TreeSet instead of a
your custom comparison methods will have to unghost the objects, the dictionary or set. Even though your custom comparison methods will
nature of a BTree means that only a small number of objects will have to unghost the objects, the nature of a BTree means that only a
have to be loaded in most cases. small number of objects will have to be loaded in most cases.
Other things you can do, but shouldn't (advanced) - Any persistent object can be wrapped in a ``zope.keyreferenece`` to
================================================= make it orderable and hashable based on persistent identity. This
can be an alternative for some dictionaries if you can't alter the
class definition but can accept identity comparisons in some
dictionaries or sets. You must remember to wrap all keys, though.
The first rule here is don't be clever!!! It's very tempting to be
clever, but it's almost never worth it.
Overriding ``__getstate__`` and ``__setstate__`` Implement ``__getstate__`` and ``__setstate__``
------------------------------------------------ -----------------------------------------------
When an object is saved in a database, its ``__getstate__`` method is When an object is saved in a database, its ``__getstate__`` method is
called without arguments to get the object's state. The default called without arguments to get the object's state. The default
...@@ -652,8 +661,8 @@ tasks like providing more efficient state representations or for ...@@ -652,8 +661,8 @@ tasks like providing more efficient state representations or for
the result was to make object implementations brittle and/or complex the result was to make object implementations brittle and/or complex
and the benefit usually wasn't worth it. and the benefit usually wasn't worth it.
Overriding ``__getattr__``, ``__getattribute__``, or ``__setattribute__`` Implement ``__getattr__``, ``__getattribute__``, or ``__setattribute__``
------------------------------------------------------------------------- ------------------------------------------------------------------------
This is something extremely clever people might attempt, but it's This is something extremely clever people might attempt, but it's
probably never worth the bother. It's possible, but it requires such probably never worth the bother. It's possible, but it requires such
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment