[ZODB-Dev] PersistentList Python 2.2-style implementation

Mike C. Fletcher mcfletch@geocities.com
Tue, 12 Feb 2002 22:45:24 -0500


This is a multi-part message in MIME format.
--------------040004000206050802060503
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Well, got the Zope-3x-branch stuff working locally (for the longest time 
I didn't realise I needed to use a branch checkout).  First thing I 
discovered I needed was a new PersistentList class (the old one was 
complaining about not getting initialised (it assumed the underlying 
list was in self.data)).

So, attached is my first crack at an implementation.  Nothing 
particularly special, just an auto-generated set of method wrappers. 
Unfortunately, this implementation (like the previous one) still doesn't 
support efficient large collections of objects.  For instance, if you 
put 10,000 objects in the list, it will load all 10,000 objects whenever 
you touch the list.

I can see a way of creating a more elaborate class, where the list just 
stores the oids, and a mapping handles actual storage of the objects so 
that doing list[x:y] would get the oids for elements x through y, then 
retrieve only those elements from the mapping.  It gets kinda messy when 
you want to delete items (lots of "if oid not in list" stuff), but not 
horribly so.  For my current project, not necessary, so I won't do it 
just now, but it might be useful as a utility class someday.

Enjoy all,
Mike
_______________________________________
   Mike C. Fletcher
   http://members.rogers.com/mcfletch/


--------------040004000206050802060503
Content-Type: text/plain;
 name="PersistentList.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="PersistentList.py"

"""Persistent List classes for ZODB3 (and ZODB2)

The new persistent list class provides a sub-class of
list which sets the _p_changed attribute of the object
when list-modifying methods are called on the list
"""

from Persistence import Persistent
class PersistentList( list, Persistent ):
	"""ZODB3 Persistent List class using Python 2.2 object inheritence

	This sub-class uses the Persistence machinery in Persistence.Persistent
	to store a list of object references.  The current implementation does
	nothing to make the storage intelligent/efficient (it just stores the
	whole list on save and restores it on load).  An implementation with a
	level of indirection allowing for not loading all items would likely
	be preferable when large numbers of (particularly) non-Persistent-derived
	objects are stored in the list.

	"""
	# don't see where this is set in Persistent if no attribute
	# accesses have occured, might be a bug in Persistent?
	_p_atime = 0
	def __delitem__( self, *arguments, **namedarguments ):
		'x.__delitem__(y) <==> del x[y]'
		value = list.__delitem__( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def __delslice__( self, *arguments, **namedarguments ):
		'x.__delslice__(i, j) <==> del x[i:j]'
		value = list.__delslice__( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def __iadd__( self, *arguments, **namedarguments ):
		'x.__iadd__(y) <==> x+=y'
		value = list.__iadd__( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def __imul__( self, *arguments, **namedarguments ):
		'x.__imul__(y) <==> x*=y'
		value = list.__imul__( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def __setitem__( self, *arguments, **namedarguments ):
		'x.__setitem__(i, y) <==> x[i]=y'
		value = list.__setitem__( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def __setslice__( self, *arguments, **namedarguments ):
		'x.__setslice__(i, j, y) <==> x[i:j]=y'
		value = list.__setslice__( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def append( self, *arguments, **namedarguments ):
		'L.append(object) -- append object to end'
		value = list.append( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def extend( self, *arguments, **namedarguments ):
		'L.extend(list) -- extend list by appending list elements'
		value = list.extend( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def insert( self, *arguments, **namedarguments ):
		'L.insert(index, object) -- insert object before index'
		value = list.insert( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def pop( self, *arguments, **namedarguments ):
		'L.pop([index]) -> item -- remove and return item at index (default last)'
		value = list.pop( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def remove( self, *arguments, **namedarguments ):
		'L.remove(value) -- remove first occurrence of value'
		value = list.remove( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def reverse( self, *arguments, **namedarguments ):
		'L.reverse() -- reverse *IN PLACE*'
		value = list.reverse( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

	def sort( self, *arguments, **namedarguments ):
		'L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1'
		value = list.sort( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value

if __name__ == '__main__':
	def generate():
		# this stuff just auto-generates the class contents from a template
		# considerably more convenient than typing it out
		template = """	def %(name)s( self, *arguments, **namedarguments ):
		%(doc)s
		value = list.%(name)s( self, *arguments, **namedarguments )
		self._p_set_changed( 1 )
		return value
"""
		for name in [
			'__delitem__',
			'__delslice__',
			'__iadd__',
			'__imul__',
			'__setitem__',
			'__setslice__',
			'append',
			'extend',
			'insert',
			'pop',
			'remove',
			'reverse',
			'sort'
		]:
			doc = repr(getattr([],name).__doc__)
			print template% globals()
	generate()

	

	

--------------040004000206050802060503--