Commit e6893159 authored by Chris Toshok's avatar Chris Toshok

Merge pull request #536 from rudi-c/setslice

Handle the extensive edge cases related to sequence slicing
parents 6add43fe 09df9da7
...@@ -302,11 +302,11 @@ ICSetupInfo* createGetitemIC(TypeRecorder* type_recorder) { ...@@ -302,11 +302,11 @@ ICSetupInfo* createGetitemIC(TypeRecorder* type_recorder) {
} }
ICSetupInfo* createSetitemIC(TypeRecorder* type_recorder) { ICSetupInfo* createSetitemIC(TypeRecorder* type_recorder) {
return ICSetupInfo::initialize(true, 1, 256, ICSetupInfo::Setitem, type_recorder); return ICSetupInfo::initialize(true, 1, 512, ICSetupInfo::Setitem, type_recorder);
} }
ICSetupInfo* createDelitemIC(TypeRecorder* type_recorder) { ICSetupInfo* createDelitemIC(TypeRecorder* type_recorder) {
return ICSetupInfo::initialize(false, 1, 256, ICSetupInfo::Delitem, type_recorder); return ICSetupInfo::initialize(false, 1, 512, ICSetupInfo::Delitem, type_recorder);
} }
ICSetupInfo* createSetattrIC(TypeRecorder* type_recorder) { ICSetupInfo* createSetattrIC(TypeRecorder* type_recorder) {
......
...@@ -134,7 +134,6 @@ static Py_ssize_t list_length(Box* self) noexcept { ...@@ -134,7 +134,6 @@ static Py_ssize_t list_length(Box* self) noexcept {
} }
Box* _listSlice(BoxedList* self, i64 start, i64 stop, i64 step, i64 length) { Box* _listSlice(BoxedList* self, i64 start, i64 stop, i64 step, i64 length) {
// printf("%ld %ld %ld\n", start, stop, step);
assert(step != 0); assert(step != 0);
if (step > 0) { if (step > 0) {
assert(0 <= start); assert(0 <= start);
...@@ -220,6 +219,16 @@ extern "C" Box* listGetitemSlice(BoxedList* self, BoxedSlice* slice) { ...@@ -220,6 +219,16 @@ extern "C" Box* listGetitemSlice(BoxedList* self, BoxedSlice* slice) {
return _listSlice(self, start, stop, step, length); 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) { extern "C" Box* listGetitem(BoxedList* self, Box* slice) {
assert(isSubclass(self->cls, list_cls)); assert(isSubclass(self->cls, list_cls));
if (PyIndex_Check(slice)) { if (PyIndex_Check(slice)) {
...@@ -278,15 +287,6 @@ extern "C" int PyList_SetItem(PyObject* op, Py_ssize_t i, PyObject* newitem) noe ...@@ -278,15 +287,6 @@ extern "C" int PyList_SetItem(PyObject* op, Py_ssize_t i, PyObject* newitem) noe
Box* listIAdd(BoxedList* self, Box* _rhs); 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 // Copied from CPython's list_ass_subscript
int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) { int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) {
Py_ssize_t start, stop, step, slicelength; Py_ssize_t start, stop, step, slicelength;
...@@ -296,8 +296,6 @@ int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) { ...@@ -296,8 +296,6 @@ int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) {
} }
RELEASE_ASSERT(step != 1, "should have handled this elsewhere"); 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: /* Make sure s[5:2] = [..] inserts at the right place:
before 5, not before 2. */ before 5, not before 2. */
...@@ -419,45 +417,10 @@ int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) { ...@@ -419,45 +417,10 @@ int list_ass_ext_slice(BoxedList* self, PyObject* item, PyObject* value) {
} }
} }
extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) { Box* listSetitemSliceInt64(BoxedList* self, i64 start, i64 stop, i64 step, Box* v) {
assert(isSubclass(self->cls, list_cls)); RELEASE_ASSERT(step == 1, "step sizes must be 1 in this code path");
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;
}
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) boundSliceWithLength(&start, &stop, start, stop, self->size);
stop = start;
else if (stop > self->size)
stop = self->size;
assert(0 <= start && start <= stop && stop <= self->size);
size_t v_size; size_t v_size;
Box** v_elts; Box** v_elts;
...@@ -479,9 +442,8 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) { ...@@ -479,9 +442,8 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
v_elts = NULL; v_elts = NULL;
} }
// If self->size is 0, self->elts->elts is garbage if (self == v) // handle self assignment by creating a copy
RELEASE_ASSERT(self->size == 0 || !v_elts || self->elts->elts != v_elts, v = _listSlice(self, 0, self->size, 1, self->size);
"Slice self-assignment currently unsupported");
int delts = v_size - (stop - start); int delts = v_size - (stop - start);
int remaining_elts = self->size - stop; int remaining_elts = self->size - stop;
...@@ -498,6 +460,39 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) { ...@@ -498,6 +460,39 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
return None; 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);
adjustNegativeIndicesOnObject(self, &start, &stop);
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) { extern "C" Box* listSetitem(BoxedList* self, Box* slice, Box* v) {
assert(isSubclass(self->cls, list_cls)); assert(isSubclass(self->cls, list_cls));
if (PyIndex_Check(slice)) { if (PyIndex_Check(slice)) {
...@@ -530,6 +525,11 @@ extern "C" Box* listDelitemSlice(BoxedList* self, BoxedSlice* slice) { ...@@ -530,6 +525,11 @@ extern "C" Box* listDelitemSlice(BoxedList* self, BoxedSlice* slice) {
return listSetitemSlice(self, slice, NULL); 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) { extern "C" Box* listDelitem(BoxedList* self, Box* slice) {
Box* rtn; Box* rtn;
if (PyIndex_Check(slice)) { if (PyIndex_Check(slice)) {
...@@ -1077,12 +1077,30 @@ void setupList() { ...@@ -1077,12 +1077,30 @@ void setupList() {
list_cls->giveAttr("__len__", new BoxedFunction(boxRTFunction((void*)listLen, BOXED_INT, 1))); 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*)listGetitemInt, UNKNOWN, std::vector<ConcreteCompilerType*>{ LIST, BOXED_INT });
addRTFunction(getitem, (void*)listGetitemSlice, LIST, std::vector<ConcreteCompilerType*>{ LIST, SLICE }); addRTFunction(getitem, (void*)listGetitemSlice, LIST, std::vector<ConcreteCompilerType*>{ LIST, SLICE });
addRTFunction(getitem, (void*)listGetitem, UNKNOWN, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN }); addRTFunction(getitem, (void*)listGetitem, UNKNOWN, std::vector<ConcreteCompilerType*>{ UNKNOWN, UNKNOWN });
list_cls->giveAttr("__getitem__", new BoxedFunction(getitem)); 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__", list_cls->giveAttr("__iter__",
new BoxedFunction(boxRTFunction((void*)listIter, typeFromClass(list_iterator_cls), 1))); new BoxedFunction(boxRTFunction((void*)listIter, typeFromClass(list_iterator_cls), 1)));
...@@ -1104,18 +1122,6 @@ void setupList() { ...@@ -1104,18 +1122,6 @@ void setupList() {
list_cls->giveAttr("append", new BoxedFunction(boxRTFunction((void*)listAppend, NONE, 2))); list_cls->giveAttr("append", new BoxedFunction(boxRTFunction((void*)listAppend, NONE, 2)));
list_cls->giveAttr("extend", new BoxedFunction(boxRTFunction((void*)listIAdd, UNKNOWN, 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("insert", new BoxedFunction(boxRTFunction((void*)listInsert, NONE, 3)));
list_cls->giveAttr("__mul__", new BoxedFunction(boxRTFunction((void*)listMul, LIST, 2))); list_cls->giveAttr("__mul__", new BoxedFunction(boxRTFunction((void*)listMul, LIST, 2)));
list_cls->giveAttr("__rmul__", new BoxedFunction(boxRTFunction((void*)listMul, LIST, 2))); list_cls->giveAttr("__rmul__", new BoxedFunction(boxRTFunction((void*)listMul, LIST, 2)));
......
This diff is collapsed.
...@@ -2250,6 +2250,18 @@ extern "C" Box* strGetitem(BoxedString* self, Box* slice) { ...@@ -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 // TODO it looks like strings don't have their own iterators, but instead
// rely on the sequence iteration protocol. // rely on the sequence iteration protocol.
...@@ -2756,6 +2768,8 @@ void setupStr() { ...@@ -2756,6 +2768,8 @@ void setupStr() {
str_cls->giveAttr("__getitem__", new BoxedFunction(boxRTFunction((void*)strGetitem, STR, 2))); 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("__iter__", new BoxedFunction(boxRTFunction((void*)strIter, typeFromClass(str_iterator_cls), 1)));
str_cls->giveAttr("replace", str_cls->giveAttr("replace",
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#include "runtime/util.h" #include "runtime/util.h"
#include "core/options.h" #include "core/options.h"
#include "core/types.h"
#include "runtime/objmodel.h" #include "runtime/objmodel.h"
#include "runtime/types.h"
namespace pyston { namespace pyston {
...@@ -25,4 +25,50 @@ void parseSlice(BoxedSlice* slice, int size, i64* out_start, i64* out_stop, i64* ...@@ -25,4 +25,50 @@ void parseSlice(BoxedSlice* slice, int size, i64* out_start, i64* out_stop, i64*
if (ret == -1) if (ret == -1)
throwCAPIException(); throwCAPIException();
} }
bool isSliceIndex(Box* b) {
return b->cls == none_cls || b->cls == int_cls || PyIndex_Check(b);
}
void adjustNegativeIndicesOnObject(Box* obj, i64* start_out, i64* stop_out) {
i64 start = *start_out;
i64 stop = *stop_out;
PySequenceMethods* m;
// Logic from PySequence_GetSlice:
m = obj->cls->tp_as_sequence;
if (m && m->sq_slice) {
if (start < 0 || stop < 0) {
if (m->sq_length) {
Py_ssize_t l = (*m->sq_length)(obj);
if (l >= 0) {
if (start < 0)
start += l;
if (stop < 0)
stop += l;
}
}
}
}
*start_out = start;
*stop_out = stop;
}
void boundSliceWithLength(i64* start_out, i64* stop_out, i64 start, i64 stop, i64 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;
}
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define PYSTON_RUNTIME_UTIL_H #define PYSTON_RUNTIME_UTIL_H
#include "core/types.h" #include "core/types.h"
#include "runtime/types.h"
namespace pyston { namespace pyston {
...@@ -23,6 +24,26 @@ class BoxedSlice; ...@@ -23,6 +24,26 @@ class BoxedSlice;
void parseSlice(BoxedSlice* slice, int size, i64* out_start, i64* out_stop, i64* out_end, i64* out_length = nullptr); void parseSlice(BoxedSlice* slice, int size, i64* out_start, i64* out_stop, i64* out_end, i64* out_length = nullptr);
// Analogue of _PyEval_SliceIndex
inline void sliceIndex(Box* b, int64_t* out) {
if (b->cls == none_cls) {
// Leave default value in case of None (useful for slices like [2:])
return;
}
int ret = _PyEval_SliceIndex(b, out);
if (ret <= 0)
throwCAPIException();
}
bool isSliceIndex(Box* b);
void adjustNegativeIndicesOnObject(Box* obj, i64* start, i64* stop);
// Adjust the start and stop bounds of the sequence we are slicing to its size.
// Ensure stop >= start and 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) { template <typename T> void copySlice(T* __restrict__ dst, const T* __restrict__ src, i64 start, i64 step, i64 length) {
assert(dst != src); assert(dst != src);
if (step == 1) { if (step == 1) {
......
...@@ -25,7 +25,7 @@ def install_and_test_protobuf(): ...@@ -25,7 +25,7 @@ def install_and_test_protobuf():
env["LD_LIBRARY_PATH"] = os.path.join(INSTALL_DIR, "lib") env["LD_LIBRARY_PATH"] = os.path.join(INSTALL_DIR, "lib")
subprocess.check_call([PYTHON_EXE, "setup.py", "build"], cwd=PROTOBUF_PY_DIR, env=env) subprocess.check_call([PYTHON_EXE, "setup.py", "build"], cwd=PROTOBUF_PY_DIR, env=env)
expected = [{"ran": 216, "failures": 0, "errors": 1}] expected = [{"ran": 216}]
run_test([PYTHON_EXE, "setup.py", "test"], cwd=PROTOBUF_PY_DIR, expected=expected, env=env) run_test([PYTHON_EXE, "setup.py", "test"], cwd=PROTOBUF_PY_DIR, expected=expected, env=env)
create_virtenv(ENV_NAME, None, force_create = True) create_virtenv(ENV_NAME, None, force_create = True)
......
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
class IndexZero(object):
def __index__(self):
return 0
def __repr__(self):
return "0"
class FalseIndex(object):
def __index__(self):
return "troll"
indexable = Indexable()
sliceable = Sliceable()
index_zero = IndexZero()
false_index = FalseIndex()
both = Both()
numbers = range(10)
letters = "abcde"
unicodestr = unicode("abcde")
# Can use index and slice notation for object with only getitem
indexable[0]
indexable[index_zero]
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)]
try:
sliceable[0]
except TypeError:
print "can't use index notation or pass in a slice for objects with only getslice"
try:
sliceable['a':'b']
except TypeError:
print "can't pass in any type into a slice with only getslice"
try:
sliceable[1:10:2]
except TypeError:
print "need getitem to support variable-sized steps"
sliceable[index_zero:index_zero]
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)
# Should all call getitem as a fallback
both['a']
both['a':'b']
both[1:'b']
both['a':2]
both[1:2:'c']
del both[0]
del both[:]
del both[3:8]
del both [::2]
try:
both[false_index:false_index]
except TypeError:
print "even if we have getitem, __index__ should not return a non-int"
# 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 letters[2]
print letters[:2]
print letters[2:]
print letters[1:3]
print letters[:-2]
print letters[-2:]
# Unicode string support slicing
# Note that unicode strings are not the same type of object as strings,
# (but both have base class basestring)
print unicodestr[2]
print unicodestr[:2]
print unicodestr[2:]
print unicodestr[1:3]
print unicodestr[:-2]
print unicodestr[-2:]
# Calling the slice operator directly does not have the same behavior
# as using the slice notation []. Namely, it will not modify negative
# indices.
print numbers.__getslice__(0, -1);
print letters.__getslice__(0, -1);
print unicodestr.__getslice__(0, -1);
# Other
class C(object): class C(object):
def __getitem__(self, idx): def __getitem__(self, idx):
print idx print idx
...@@ -17,3 +187,4 @@ print sl ...@@ -17,3 +187,4 @@ print sl
C()[:,:] C()[:,:]
C()[1:2,3:4] C()[1:2,3:4]
C()[1:2:3,3:4:5] 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