[C++-sig] Re: New slice implementation

Jonathan Brandmeyer jbrandmeyer at earthlink.net
Fri Jan 9 17:30:17 CET 2004


On Fri, 2004-01-09 at 08:19, Raoul Gough wrote:
> Jonathan Brandmeyer <jbrandmeyer at earthlink.net> writes:
> 
> > On Thu, 2004-01-08 at 20:55, Raoul Gough wrote:
> [snip]
> >> part = v[1::4]
> >> 
> >> calls v.__getitem__ with a PySliceObject (1, None, 4)
> >
> > *I* won't end up with a PySliceObject created by the Python interpreter,
> > I will end up with a boost::python::slice object manager that has been
> > automatically converted by def() and/or class_::def() from the original
> > PySliceObject.
> 
> Well, that's the bit I don't understand. How do you create a
> boost::python::slice object from a PySliceObject without a suitable
> constructor? Unless you're just not interested in covering this case.

What I believe happens is that PySlice_Check() is called on the
PyObject* that is being tested to see if it can be wrapped.  If true,
then a slice object is created by calling boost::python::slice(
detail::borrowed_reference(PyObject*)).  Then, if you want to get the
objects that the slice was defined with by calling slice::start(),
slice::stop(), or slice::step(), each of those functions must cast the
wrapped PyObject* to a PySliceObject*.  This is guaranteed to be safe
since the slice object is never created without either a) creating a new
PySliceObject, or b) calling PySlice_Check() first.  To the best of my
knowledge, that's how most of the object managers work.

Can you give me a complete example where you need this extra
constructor?  Maybe then I'll know how to answer you better.


> >> >> > # I think this should raise IndexError; crashes.
> >> >> > foo[-1:0] = [7, 8, 9]
> >> >> 
> >> >> With a real python list, it inserts 7, 8, 9 before the last element:
> >> >> 
> >> >> >>> v = [1,2,3,4]
> >> >> >>> v[-1:0] = [7,8,9]
> >> >> >>> print v
> >> >> [1, 2, 3, 7, 8, 9, 4]
> >> >
> >> > Yes, that is what happens: performing an insertion before the provided
> >> > start value.  However, I think that it should be an undefined operation
> >> > since the expression is utter nonsense.  I've looked at the source code
> >> > for PyListObject, and I think that this behavior is the result of bounds
> >> > limiting rather than real error checking.
> >> 
> >> I don't understand this. Assigning something into an empty slice in a
> >> container always performs an insertion. More generally, assigning a
> >> longer list to a plain slice with fewer elements performs insertion.
> >> e.g.
> >
> > No, assigning any sequence to a slice by calling 
> > object.__setslice__(slice, sequence) replaces the old slice with the
> > values of the new slice IF the slice uses either -1 or 1 (either
> > implicitly or explicitly) for its step size.  If the step size is
> > non-singular, then the sizes of the slice and the sequence must be
> > identical.
> 
> The lack of a step size is what I was getting at with "plain slice". I
> guess I should have been more precise.
> 
> >
> >> >>> l = [1,2,3,4]
> >> >>> l[3:0] = [7,8,9]
> >
> > Think about what this expression means: Starting at the fourth element
> > inclusive, and ending at the first element, exclusive with an increment
> > of forward 1, delete elements and replace them with the elements of the
> > list [7,8,9].  That's like starting off by calling
> > std::list::delete(start, stop) with a 'stop' iterator that is not
> > reachable from 'start'!
> 
> But that's not the way *Python* does things. Are you saying that
> lst[3:0] should just crash the interpreter? That's what a C++
> algorithm would probably do with that kind of input.

No, I'm saying that lst[3:0] should be empty.  The difference is that in
the Python case we can immediately determine that the stop position is
not reachable from the start position and take appropriate action: Do
Nothing.

> >
> > The issue isn't that you are performing an insertion after a
> > well-defined point in the sequence, it is that you are performing a
> > replacement of an undefined section of the sequence.
> 
> I would argue that the section of the sequence *is* well defined.
> 

> >
> >> >
> >> > See Python bug# 873305 at 
> > http://sourceforge.net/tracker/index.php?func=detail&aid=873305&group_id=5470&atid=305470
> >
> 
> I don't think I agree with what you're saying here. Would you agree
> that __getitem__ from e.g. lst[4:2] should be an empty sequence?
> 
> Quoting from http://www.python.org/doc/current/lib/typesseq.html
> 
> "(4) [...] If i is greater than or equal to j, the slice is empty."

Section 5.3.3 of the Python Language Reference includes:  "The slicing
now selects all items with index k such that i <= k < j where i and j
are the specified lower and upper bounds. This may be an empty
sequence."  

I claim that not only are there no elements to be replaced in the case
of __setitem__ with such a slice, but that this doesn't clearly define a
point to insert new elements, either.

> 
> I think that's pretty clear for __getitem__. 

Yes, and my patch doesn't modify that behavior.

> So now the question is,
> what is the result of replacing an empty slice in a container with a
> non-empty sequence? If the container supports insertion, inserting the
> sequence seems logical enough to me.

So how do you define the point to perform the insertion?  If the user
asks to replace every element that is greater than or equal to the
fourth and less than the first element, where do you place the new
sequence?

The current action for a built-in list is to range-limit the stop point
to be not less than the start point, but I think that is just for safety
rather than an API decision, and that it is wrong.

-Jonathan Brandmeyer





More information about the Cplusplus-sig mailing list