Commit 987fa9ec authored by Rudi Chen's avatar Rudi Chen

Implement missing functionality for indexing and slicing.

- Call either the item functions (e.g. __getitem__) or slicing function
  (e.g. __getslice__) to match the CPython behavior.
- Implement __getslice, __setslice__, __delslice__, for lists.
- Implement __setslice__ for strings.
- Some refactoring of common functionality for indexing and slicing.
- A lot of tests.
parent 9d188a18
......@@ -220,6 +220,16 @@ extern "C" Box* listGetitemSlice(BoxedList* self, BoxedSlice* slice) {
return _listSlice(self, start, stop, step, length);
}
extern "C" Box* listGetslice(BoxedList* self, Box* boxedStart, Box* boxedStop) {
assert(isSubclass(self->cls, list_cls));
i64 start, stop, step;
sliceIndex(boxedStart, &start);
sliceIndex(boxedStop, &stop);
boundSliceWithLength(&start, &stop, start, stop, self->size);
return _listSlice(self, start, stop, 1, stop - start);
}
extern "C" Box* listGetitem(BoxedList* self, Box* slice) {
assert(isSubclass(self->cls, list_cls));
if (PyIndex_Check(slice)) {
......@@ -278,15 +288,6 @@ extern "C" int PyList_SetItem(PyObject* op, Py_ssize_t i, PyObject* newitem) noe
Box* listIAdd(BoxedList* self, Box* _rhs);
// Analogue of _PyEval_SliceIndex
static void sliceIndex(Box* b, int64_t* out) {
if (b->cls == none_cls)
return;
RELEASE_ASSERT(b->cls == int_cls, "");
*out = static_cast<BoxedInt*>(b)->n;
}
// Copied from CPython's list_ass_subscript
int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) {
Py_ssize_t start, stop, step, slicelength;
......@@ -296,8 +297,6 @@ int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) {
}
RELEASE_ASSERT(step != 1, "should have handled this elsewhere");
// if (step == 1)
// return list_ass_slice(self, start, stop, value);
/* Make sure s[5:2] = [..] inserts at the right place:
before 5, not before 2. */
......@@ -419,45 +418,10 @@ int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) {
}
}
extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
assert(isSubclass(self->cls, list_cls));
assert(slice->cls == slice_cls);
i64 start = 0, stop = self->size, step = 1;
sliceIndex(slice->start, &start);
sliceIndex(slice->stop, &stop);
sliceIndex(slice->step, &step);
if (self == v) // handle self assignment by creating a copy
v = _listSlice(self, 0, self->size, 1, self->size);
if (step != 1) {
int r = list_ass_ext_slice(self, slice, v);
if (r)
throwCAPIException();
return None;
}
Box* listSetitemSliceInt64(BoxedList* self, i64 start, i64 stop, i64 step, Box* v) {
RELEASE_ASSERT(step == 1, "step sizes must be 1 for now");
// Logic from PySequence_GetSlice:
if (start < 0)
start += self->size;
if (stop < 0)
stop += self->size;
// Logic from list_ass_slice:
if (start < 0)
start = 0;
else if (start > self->size)
start = self->size;
if (stop < start)
stop = start;
else if (stop > self->size)
stop = self->size;
assert(0 <= start && start <= stop && stop <= self->size);
boundSliceWithLength(&start, &stop, start, stop, self->size);
size_t v_size;
Box** v_elts;
......@@ -479,9 +443,8 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
v_elts = NULL;
}
// If self->size is 0, self->elts->elts is garbage
RELEASE_ASSERT(self->size == 0 || !v_elts || self->elts->elts != v_elts,
"Slice self-assignment currently unsupported");
if (self == v) // handle self assignment by creating a copy
v = _listSlice(self, 0, self->size, 1, self->size);
int delts = v_size - (stop - start);
int remaining_elts = self->size - stop;
......@@ -498,6 +461,37 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
return None;
}
extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
assert(isSubclass(self->cls, list_cls));
assert(slice->cls == slice_cls);
i64 start = 0, stop = self->size, step = 1;
sliceIndex(slice->start, &start);
sliceIndex(slice->stop, &stop);
sliceIndex(slice->step, &step);
if (step != 1) {
int r = list_ass_ext_slice(self, slice, v);
if (r)
throwCAPIException();
return None;
}
return listSetitemSliceInt64(self, start, stop, step, v);
}
extern "C" Box* listSetslice(BoxedList* self, Box* boxedStart, Box* boxedStop, Box** args) {
Box* value = args[0];
i64 start = 0, stop = self->size;
sliceIndex(boxedStart, &start);
sliceIndex(boxedStop, &stop);
return listSetitemSliceInt64(self, start, stop, 1, value);
}
extern "C" Box* listSetitem(BoxedList* self, Box* slice, Box* v) {
assert(isSubclass(self->cls, list_cls));
if (PyIndex_Check(slice)) {
......@@ -530,6 +524,11 @@ extern "C" Box* listDelitemSlice(BoxedList* self, BoxedSlice* slice) {
return listSetitemSlice(self, slice, NULL);
}
extern "C" Box* listDelslice(BoxedList* self, Box* start, Box* stop) {
Box* args = { NULL };
return listSetslice(self, start, stop, &args);
}
extern "C" Box* listDelitem(BoxedList* self, Box* slice) {
Box* rtn;
if (PyIndex_Check(slice)) {
......@@ -1077,12 +1076,30 @@ void setupList() {
list_cls->giveAttr("__len__", new BoxedFunction(boxRTFunction((void*)listLen, BOXED_INT, 1)));
CLFunction* getitem = createRTFunction(2, 0, 0, 0);
CLFunction* getitem = createRTFunction(2, 0, false, false);
addRTFunction(getitem, (void*)listGetitemInt, UNKNOWN, std::vector<ConcreteCompilerType*>{ LIST, BOXED_INT });
addRTFunction(getitem, (void*)listGetitemSlice, LIST, std::vector<ConcreteCompilerType*>{ LIST, SLICE });
addRTFunction(getitem, (void*)listGetitem, UNKNOWN, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN });
list_cls->giveAttr("__getitem__", new BoxedFunction(getitem));
list_cls->giveAttr("__getslice__", new BoxedFunction(boxRTFunction((void*)listGetslice, LIST, 3)));
CLFunction* setitem = createRTFunction(3, 0, false, false);
addRTFunction(setitem, (void*)listSetitemInt, NONE, std::vector<ConcreteCompilerType*>{ LIST, BOXED_INT, UNKNOWN });
addRTFunction(setitem, (void*)listSetitemSlice, NONE, std::vector<ConcreteCompilerType*>{ LIST, SLICE, UNKNOWN });
addRTFunction(setitem, (void*)listSetitem, NONE, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN, UNKNOWN });
list_cls->giveAttr("__setitem__", new BoxedFunction(setitem));
list_cls->giveAttr("__setslice__", new BoxedFunction(boxRTFunction((void*)listSetslice, NONE, 4)));
CLFunction* delitem = createRTFunction(2, 0, false, false);
addRTFunction(delitem, (void*)listDelitemInt, NONE, std::vector<ConcreteCompilerType*>{ LIST, BOXED_INT });
addRTFunction(delitem, (void*)listDelitemSlice, NONE, std::vector<ConcreteCompilerType*>{ LIST, SLICE });
addRTFunction(delitem, (void*)listDelitem, NONE, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN });
list_cls->giveAttr("__delitem__", new BoxedFunction(delitem));
list_cls->giveAttr("__delslice__", new BoxedFunction(boxRTFunction((void*)listDelslice, NONE, 3)));
list_cls->giveAttr("__iter__",
new BoxedFunction(boxRTFunction((void*)listIter, typeFromClass(list_iterator_cls), 1)));
......@@ -1104,18 +1121,6 @@ void setupList() {
list_cls->giveAttr("append", new BoxedFunction(boxRTFunction((void*)listAppend, NONE, 2)));
list_cls->giveAttr("extend", new BoxedFunction(boxRTFunction((void*)listIAdd, UNKNOWN, 2)));
CLFunction* setitem = createRTFunction(3, 0, false, false);
addRTFunction(setitem, (void*)listSetitemInt, NONE, std::vector<ConcreteCompilerType*>{ LIST, BOXED_INT, UNKNOWN });
addRTFunction(setitem, (void*)listSetitemSlice, NONE, std::vector<ConcreteCompilerType*>{ LIST, SLICE, UNKNOWN });
addRTFunction(setitem, (void*)listSetitem, NONE, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN, UNKNOWN });
list_cls->giveAttr("__setitem__", new BoxedFunction(setitem));
CLFunction* delitem = createRTFunction(2, 0, false, false);
addRTFunction(delitem, (void*)listDelitemInt, NONE, std::vector<ConcreteCompilerType*>{ LIST, BOXED_INT });
addRTFunction(delitem, (void*)listDelitemSlice, NONE, std::vector<ConcreteCompilerType*>{ LIST, SLICE });
addRTFunction(delitem, (void*)listDelitem, NONE, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN });
list_cls->giveAttr("__delitem__", new BoxedFunction(delitem));
list_cls->giveAttr("insert", new BoxedFunction(boxRTFunction((void*)listInsert, NONE, 3)));
list_cls->giveAttr("__mul__", new BoxedFunction(boxRTFunction((void*)listMul, LIST, 2)));
list_cls->giveAttr("__rmul__", new BoxedFunction(boxRTFunction((void*)listMul, LIST, 2)));
......
......@@ -4365,7 +4365,79 @@ extern "C" Box* unaryop(Box* operand, int op_type) {
return rtn;
}
extern "C" Box* getitem(Box* value, Box* slice) {
// This function decides whether to call the slice operator (e.g. __getslice__)
// or the item operator (__getitem__).
Box* callItemOrSliceAttr(Box* target, BoxedString* item_str, BoxedString* slice_str, Box* slice, Box* value,
CallRewriteArgs* rewrite_args) {
// If we don't have a slice operator, fall back to item operator.
// No rewriting for the typeLookup here (i.e. no guards if there is no slice operator). These guards
// will be added when we call callattrInternal on a slice operator later. If the guards fail, we fall
// back into the slow path which has this fallback to the item operator.
Box* slice_attr = typeLookup(target->cls, slice_str, NULL);
if (!slice_attr) {
if (value) {
return callattrInternal2(target, item_str, CLASS_ONLY, rewrite_args, ArgPassSpec(2), slice, value);
} else {
return callattrInternal1(target, item_str, CLASS_ONLY, rewrite_args, ArgPassSpec(1), slice);
}
}
// Need a slice object to use the slice operators.
if (slice->cls != slice_cls) {
if (rewrite_args) {
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (uint64_t)slice_cls, /*negate=*/true);
}
if (value) {
return callattrInternal2(target, item_str, CLASS_ONLY, rewrite_args, ArgPassSpec(2), slice, value);
} else {
return callattrInternal1(target, item_str, CLASS_ONLY, rewrite_args, ArgPassSpec(1), slice);
}
} else {
if (rewrite_args) {
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (uint64_t)slice_cls);
}
}
BoxedSlice* bslice = (BoxedSlice*)slice;
// If we use slice notation with a step parameter (e.g. o[1:10:2]), the slice operator
// functions don't support that, so fallback to the item operator functions.
if (bslice->step->cls != none_cls) {
if (rewrite_args) {
rewrite_args->arg1->getAttr(offsetof(BoxedSlice, step))
->addAttrGuard(offsetof(Box, cls), (uint64_t)none_cls, /*negate=*/true);
}
if (value) {
return callattrInternal2(target, item_str, CLASS_ONLY, rewrite_args, ArgPassSpec(2), slice, value);
} else {
return callattrInternal1(target, item_str, CLASS_ONLY, rewrite_args, ArgPassSpec(1), slice);
}
} else {
rewrite_args = NULL;
REWRITE_ABORTED("");
Box* start = bslice->start;
Box* stop = bslice->stop;
// If we don't specify the start/stop (e.g. o[:]), the slice operator functions
// CPython seems to use 0 and sys.maxint as the default values.
if (bslice->start->cls == none_cls)
start = boxInt(0);
if (bslice->stop->cls == none_cls)
stop = boxInt(PyInt_GetMax());
if (value) {
return callattrInternal3(target, slice_str, CLASS_ONLY, rewrite_args, ArgPassSpec(3), start, stop, value);
} else {
return callattrInternal2(target, slice_str, CLASS_ONLY, rewrite_args, ArgPassSpec(2), start, stop);
}
}
}
// target[slice]
extern "C" Box* getitem(Box* target, Box* slice) {
STAT_TIMER(t0, "us_timer_slowpath_getitem", 10);
// This possibly could just be represented as a single callattr; the only tricky part
......@@ -4386,7 +4458,7 @@ extern "C" Box* getitem(Box* value, Box* slice) {
// For now, just use the first clause: call mp_subscript if it exists.
// And only if we think it's better than calling __getitem__, which should
// exist if mp_subscript exists.
PyMappingMethods* m = value->cls->tp_as_mapping;
PyMappingMethods* m = target->cls->tp_as_mapping;
if (m && m->mp_subscript && m->mp_subscript != slot_mp_subscript) {
if (rewriter.get()) {
RewriterVar* r_obj = rewriter->getArg(0);
......@@ -4405,38 +4477,41 @@ extern "C" Box* getitem(Box* value, Box* slice) {
rewriter->call(true, (void*)checkAndThrowCAPIException);
rewriter->commitReturning(r_rtn);
}
Box* r = m->mp_subscript(value, slice);
Box* r = m->mp_subscript(target, slice);
if (!r)
throwCAPIException();
return r;
}
static BoxedString* getitem_str = internStringImmortal("__getitem__");
static BoxedString* getslice_str = internStringImmortal("__getslice__");
Box* rtn;
if (rewriter.get()) {
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getReturnDestination());
rewrite_args.arg1 = rewriter->getArg(1);
rtn = callattrInternal1(value, getitem_str, CLASS_ONLY, &rewrite_args, ArgPassSpec(1), slice);
rtn = callItemOrSliceAttr(target, getitem_str, getslice_str, slice, NULL, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
} else if (rtn)
} else if (rtn) {
rewriter->commitReturning(rewrite_args.out_rtn);
}
} else {
rtn = callattrInternal1(value, getitem_str, CLASS_ONLY, NULL, ArgPassSpec(1), slice);
rtn = callItemOrSliceAttr(target, getitem_str, getslice_str, slice, NULL, NULL);
}
if (rtn == NULL) {
// different versions of python give different error messages for this:
if (PYTHON_VERSION_MAJOR == 2 && PYTHON_VERSION_MINOR < 7) {
raiseExcHelper(TypeError, "'%s' object is unsubscriptable", getTypeName(value)); // tested on 2.6.6
raiseExcHelper(TypeError, "'%s' object is unsubscriptable", getTypeName(target)); // tested on 2.6.6
} else if (PYTHON_VERSION_MAJOR == 2 && PYTHON_VERSION_MINOR == 7 && PYTHON_VERSION_MICRO < 3) {
raiseExcHelper(TypeError, "'%s' object is not subscriptable", getTypeName(value)); // tested on 2.7.1
raiseExcHelper(TypeError, "'%s' object is not subscriptable", getTypeName(target)); // tested on 2.7.1
} else {
// Changed to this in 2.7.3:
raiseExcHelper(TypeError, "'%s' object has no attribute '__getitem__'",
getTypeName(value)); // tested on 2.7.3
getTypeName(target)); // tested on 2.7.3
}
}
......@@ -4454,6 +4529,7 @@ extern "C" void setitem(Box* target, Box* slice, Box* value) {
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "setitem"));
static BoxedString* setitem_str = internStringImmortal("__setitem__");
static BoxedString* setslice_str = internStringImmortal("__setslice__");
Box* rtn;
if (rewriter.get()) {
......@@ -4461,25 +4537,24 @@ extern "C" void setitem(Box* target, Box* slice, Box* value) {
rewrite_args.arg1 = rewriter->getArg(1);
rewrite_args.arg2 = rewriter->getArg(2);
rtn = callattrInternal2(target, setitem_str, CLASS_ONLY, &rewrite_args, ArgPassSpec(2), slice, value);
rtn = callItemOrSliceAttr(target, setitem_str, setslice_str, slice, value, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
}
} else {
rtn = callattrInternal2(target, setitem_str, CLASS_ONLY, NULL, ArgPassSpec(2), slice, value);
rtn = callItemOrSliceAttr(target, setitem_str, setslice_str, slice, value, NULL);
}
if (rtn == NULL) {
raiseExcHelper(TypeError, "'%s' object does not support item assignment", getTypeName(target));
}
if (rewriter.get()) {
if (rewriter.get())
rewriter->commit();
}
}
// del target[start:end:step]
// del target[slice]
extern "C" void delitem(Box* target, Box* slice) {
STAT_TIMER(t0, "us_timer_slowpath_delitem", 10);
......@@ -4490,29 +4565,28 @@ extern "C" void delitem(Box* target, Box* slice) {
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 2, "delitem"));
static BoxedString* delitem_str = internStringImmortal("__delitem__");
static BoxedString* delslice_str = internStringImmortal("__delslice__");
Box* rtn;
if (rewriter.get()) {
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getReturnDestination());
rewrite_args.arg1 = rewriter->getArg(1);
rtn = callattrInternal1(target, delitem_str, CLASS_ONLY, &rewrite_args, ArgPassSpec(1), slice);
rtn = callItemOrSliceAttr(target, delitem_str, delslice_str, slice, NULL, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
}
} else {
rtn = callattrInternal1(target, delitem_str, CLASS_ONLY, NULL, ArgPassSpec(1), slice);
rtn = callItemOrSliceAttr(target, delitem_str, delslice_str, slice, NULL, NULL);
}
if (rtn == NULL) {
raiseExcHelper(TypeError, "'%s' object does not support item deletion", getTypeName(target));
}
if (rewriter.get()) {
if (rewriter.get())
rewriter->commit();
}
}
void Box::delattr(BoxedString* attr, DelattrRewriteArgs* rewrite_args) {
......
......@@ -2250,6 +2250,18 @@ extern "C" Box* strGetitem(BoxedString* self, Box* slice) {
}
}
extern "C" Box* strGetslice(BoxedString* self, Box* boxedStart, Box* boxedStop) {
assert(isSubclass(self->cls, str_cls));
i64 start, stop;
sliceIndex(boxedStart, &start);
sliceIndex(boxedStop, &stop);
boundSliceWithLength(&start, &stop, start, stop, self->s().size());
return _strSlice(self, start, stop, 1, stop - start);
}
// TODO it looks like strings don't have their own iterators, but instead
// rely on the sequence iteration protocol.
......@@ -2756,6 +2768,8 @@ void setupStr() {
str_cls->giveAttr("__getitem__", new BoxedFunction(boxRTFunction((void*)strGetitem, STR, 2)));
str_cls->giveAttr("__getslice__", new BoxedFunction(boxRTFunction((void*)strGetslice, STR, 3)));
str_cls->giveAttr("__iter__", new BoxedFunction(boxRTFunction((void*)strIter, typeFromClass(str_iterator_cls), 1)));
str_cls->giveAttr("replace",
......
......@@ -15,6 +15,7 @@
#include "runtime/util.h"
#include "core/options.h"
#include "core/types.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
......@@ -25,4 +26,42 @@ void parseSlice(BoxedSlice* slice, int size, i64* out_start, i64* out_stop, i64*
if (ret == -1)
throwCAPIException();
}
void sliceIndex(Box* b, int64_t* out) {
if (b->cls == none_cls) {
// Leave default value in case of None (useful for slices like [2:])
} else if (b->cls == int_cls) {
*out = static_cast<BoxedInt*>(b)->n;
} else if (PyIndex_Check(b)) {
int64_t x = PyNumber_AsSsize_t(b, NULL);
if (!(x == -1 && PyErr_Occurred()))
*out = x;
} else {
raiseExcHelper(TypeError, "slice indices must be integers or "
"None or have an __index__ method");
}
}
void boundSliceWithLength(i64* start_out, i64* stop_out, i64 start, i64 stop, i64 size) {
// Logic from PySequence_GetSlice:
if (start < 0)
start += size;
if (stop < 0)
stop += size;
if (start < 0)
start = 0;
else if (start > size)
start = size;
if (stop < start)
stop = start;
else if (stop > size)
stop = size;
assert(0 <= start && start <= stop && stop <= size);
*start_out = start;
*stop_out = stop;
}
}
......@@ -23,6 +23,15 @@ class BoxedSlice;
void parseSlice(BoxedSlice* slice, int size, i64* out_start, i64* out_stop, i64* out_end, i64* out_length = nullptr);
// Analogue of _PyEval_SliceIndex
void sliceIndex(Box* b, int64_t* out);
// Adjust the start and stop bounds of the sequence we are slicing to its size.
// Negative values greater or equal to (-length) become positive values.
// Ensure stop >= start
// Remain within bounds.
void boundSliceWithLength(i64* start_out, i64* stop_out, i64 start, i64 stop, i64 size);
template <typename T> void copySlice(T* __restrict__ dst, const T* __restrict__ src, i64 start, i64 step, i64 length) {
assert(dst != src);
if (step == 1) {
......
class Indexable(object):
def __getitem__(self, idx):
print "called getitem on object", idx
def __delitem__(self, idx):
print "called delitem on object", idx
def __setitem__(self, idx, item):
print "called setitem on object", idx
class Sliceable(object):
def __getslice__(self, start, stop):
print "called getslice on object", start, stop
def __delslice__(self, start, stop):
print "called delslice on object", start, stop
def __setslice__(self, start, stop, item):
print "called setslice on object", start, stop
class Both(object):
def __getitem__(self, idx):
print "called getitem on object", idx
def __delitem__(self, idx):
print "called delitem on object", idx
def __setitem__(self, idx, item):
print "called setitem on object", idx
def __getslice__(self, start, stop):
print "called getslice on object", start, stop
def __delslice__(self, start, stop):
print "called delslice on object", start, stop
def __setslice__(self, start, stop, item):
print "called setslice on object", start, stop
indexable = Indexable()
sliceable = Sliceable()
both = Both()
numbers = range(10)
# Can use index and slice notation for object with only getitem
indexable[0]
indexable[:10]
indexable[11:]
indexable[:]
indexable[3:8]
indexable[slice(1,2)]
indexable[slice(1,12,2)]
indexable[0] = 32
indexable[:] = xrange(2)
indexable[3:8] = xrange(2)
indexable[slice(1,12,2)] = xrange(2)
del indexable[0]
del indexable[:]
del indexable[3:8]
del indexable[slice(1,12,2)]
# Can't use index notation or pass in a slice for objects with only getslice
try:
sliceable[0]
except TypeError:
print "no index notation with index"
try:
sliceable[1:10:2]
except TypeError:
print "need getitem to support variable-sized steps"
sliceable[:10]
sliceable[11:]
sliceable[:]
sliceable[3:8]
# Make sure the right function gets called when both are present
both[0]
both[:]
both[3:8]
both[::2] # this should call __getitem__ since __getslice__ doesn't support steps
both[0] = xrange(2)
both[:] = xrange(2)
both[3:8] = xrange(2)
both[::2] = xrange(2)
del both[0]
del both[:]
del both[3:8]
del both [::2]
# Number lists should have the set/get/del|item/slice functions
print numbers[0]
print numbers[:]
print numbers[1:9]
numbers[0] = 42
numbers[7:8] = xrange(8)
print numbers
del numbers[0]
del numbers[5:6]
print numbers
# Number lists should support negative indices
print numbers[-1]
print numbers[-1:-1]
print numbers[:-2]
print numbers[-2:]
# String support slicing
print "abcd"[2]
print "abcd"[:2]
print "abcd"[2:]
print "abcd"[1:3]
# Other
class C(object):
def __getitem__(self, idx):
print idx
......@@ -17,3 +136,4 @@ print sl
C()[:,:]
C()[1:2,3:4]
C()[1:2:3,3:4:5]
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