UserList.__getslice__(): copy.copy(self.data) vs. self.__class__(self.data).

Bernhard Herzog herzog at online.de
Wed Mar 15 00:25:29 CET 2000


Tom Funk <_spam_sux_tdfunk at _spam_sux_nettally.com> writes:

> In the Python Reference Manual, Section 3.3.5, "Additional methods for 
> emulation of sequence types", we find the following entry:
>  
>   ...
> 
>   __getslice__ (self, i, j) 
> 
>   Called to implement evaluation of self[i:j]. The returned 
>   object should be of the same type as self. Note that missing 
>   i or j in the slice expression are replaced by zero or 
>   sys.maxint, respectively, and no further transformations on 
>   the indices is performed. The interpretation of negative 
>   indices and indices larger than the length of the sequence 
>   is up to the method. 

That appears to be incorrect in Python 1.5.2. Using a slightly modified
UserList.py that prints the i and j args of the __getslice__ method, I
get:

Python 1.5.2 (#1, Nov 13 1999, 12:17:58)  [GCC egcs-2.91.66 19990314/Linux (egcs- on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> from UserList2 import UserList
>>> l = [0,1,2,3,4,5]
>>> u = UserList(l)
>>> u[-2:-1]
__getslice__ 4 5
[4]
>>> 

It seems negative indices are automatically subtracted from the length.

> The current implementation of UserList.__getslice__(),  looks like this:
> 
>     def __getslice__(self, i, j):
>         i = max(i, 0); j = max(j, 0)
>         userlist = self.__class__()
>         userlist.data[:] = self.data[i:j]
>         return userlist
> 
> Though this follows the guidelines outlined in the reference manual, it 
> has an interesting side effect: it instantiates a new object of the same 
> class but it loses the current values of all attributes.  
> 
> Is this desireable behavior?   Personally, I don't believe that it is.  

UserList has only one instance variable, data, and that is handled
correctly. If you need to copy more variables in a derived class you
should probably override this method, IMO.

[...]
> Also, should the i and j arguments be "adjusted" before being used to 
> access the list in self.data?  Again, in the Python Reference Manual, 
> section 5.3.3 "Slicings," we find:
> 
>   The lower and upper bound expressions, if present, must evaluate 
>   to plain integers; defaults are zero and the sequence's length,
>   respectively. If either bound is negative, the sequence's length 
>   is added to it. 

It's not really relevant for this post, but note, that this only applies
to simple slicings, i.e. those of the form lower:upper. Extended
slicings, i-e- those with two colons, are handled differently.

> So, the runtime normalizes negative numbers so that small-enough (large 
> enough??<g>) negative numbers start counting from the end of the 
> sequence.  i.e., aList[-1] returns the last element in the aList.  As 
> currently written, UserList converts to zero any negative numbers that 
> would otherwise raise an IndexError. 

Negative i and j are mapped to 0 because they're already the result of
subtracting them from the length. If they're still negative when
__getslice__ is called they would again be subtracted from the length of
self.data and that would likely produce incorrect results. The correct
result in that case is an empty list.

Note that all slicings succeed for normal lists. If both indices of the
slice are too large or too small (i.e. too negative ;-) ) the result is
an empty list.

> The resulting behavior is that a 
> slice is returned rather than an IndexError being raised, thus:
> 
>   >>> ul=UserList.UserList([0,1,2,3,4])
>   >>> ul[-1] # last element
>   4
>   >>> ul[-3:-1] # 3rd- and 2nd-to-the-last elements
>   [2,3]
>   >>> ul[-10] # calls UserList__getitem__(self,i)
>   Traceback (innermost last):
>     File "<interactive input>", line 1, in ?
>     File "UserList.py", line 29, in __getitem__
>       def __delitem__(self, i): del self.data[i]
>   IndexError: list index out of range
>   >>> ul[-10:-1] # should raise IndexError
>   [0, 1, 2, 3]

A normal list doesn't raise an IndexError here. It returns [0, 1, 2, 3]
Even ul[-10:-100] succeeds for a normal list, returning [] .


-- 
Bernhard Herzog   | Sketch, a drawing program for Unix
herzog at online.de  | http://sketch.sourceforge.net/



More information about the Python-list mailing list