Generalize list(seq) to work with iterators. This also generalizes list()
to no longer insist that len(seq) be defined. NEEDS DOC CHANGES. This is meant to be a model for how other functions of this ilk (max, filter, etc) can be generalized similarly. Feel encouraged to grab your favorite and convert it! Note some cute consequences: list(file) == file.readlines() == list(file.xreadlines()) list(dict) == dict.keys() list(dict.iteritems()) = dict.items() list(xrange(i, j, k)) == range(i, j, k)
This commit is contained in:
parent
47668928e6
commit
f553f89d45
|
@ -243,4 +243,36 @@ class TestCase(unittest.TestCase):
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Test list()'s use of iterators.
|
||||||
|
def test_builtin_list(self):
|
||||||
|
self.assertEqual(list(SequenceClass(5)), range(5))
|
||||||
|
self.assertEqual(list(SequenceClass(0)), [])
|
||||||
|
self.assertEqual(list(()), [])
|
||||||
|
self.assertEqual(list(range(10, -1, -1)), range(10, -1, -1))
|
||||||
|
|
||||||
|
d = {"one": 1, "two": 2, "three": 3}
|
||||||
|
self.assertEqual(list(d), d.keys())
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, list, list)
|
||||||
|
self.assertRaises(TypeError, list, 42)
|
||||||
|
|
||||||
|
f = open(TESTFN, "w")
|
||||||
|
try:
|
||||||
|
for i in range(5):
|
||||||
|
f.write("%d\n" % i)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
f = open(TESTFN, "r")
|
||||||
|
try:
|
||||||
|
self.assertEqual(list(f), ["0\n", "1\n", "2\n", "3\n", "4\n"])
|
||||||
|
f.seek(0, 0)
|
||||||
|
self.assertEqual(list(f.xreadlines()),
|
||||||
|
["0\n", "1\n", "2\n", "3\n", "4\n"])
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
try:
|
||||||
|
unlink(TESTFN)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
run_unittest(TestCase)
|
run_unittest(TestCase)
|
||||||
|
|
10
Misc/NEWS
10
Misc/NEWS
|
@ -1,3 +1,13 @@
|
||||||
|
What's New in Python 2.2a0?
|
||||||
|
===========================
|
||||||
|
|
||||||
|
Core
|
||||||
|
|
||||||
|
- The following functions were generalized to work nicely with iterator
|
||||||
|
arguments:
|
||||||
|
list()
|
||||||
|
|
||||||
|
|
||||||
What's New in Python 2.1 (final)?
|
What's New in Python 2.1 (final)?
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
|
|
@ -1236,52 +1236,78 @@ PySequence_Tuple(PyObject *v)
|
||||||
PyObject *
|
PyObject *
|
||||||
PySequence_List(PyObject *v)
|
PySequence_List(PyObject *v)
|
||||||
{
|
{
|
||||||
PySequenceMethods *m;
|
PyObject *it; /* iter(v) */
|
||||||
|
PyObject *result; /* result list */
|
||||||
|
int n; /* guess for result list size */
|
||||||
|
int i;
|
||||||
|
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
return null_error();
|
return null_error();
|
||||||
|
|
||||||
|
/* Special-case list(a_list), for speed. */
|
||||||
if (PyList_Check(v))
|
if (PyList_Check(v))
|
||||||
return PyList_GetSlice(v, 0, PyList_GET_SIZE(v));
|
return PyList_GetSlice(v, 0, PyList_GET_SIZE(v));
|
||||||
|
|
||||||
m = v->ob_type->tp_as_sequence;
|
/* Get iterator. There may be some low-level efficiency to be gained
|
||||||
if (m && m->sq_item) {
|
* by caching the tp_iternext slot instead of using PyIter_Next()
|
||||||
int i;
|
* later, but premature optimization is the root etc.
|
||||||
PyObject *l;
|
*/
|
||||||
int n = PySequence_Size(v);
|
it = PyObject_GetIter(v);
|
||||||
|
if (it == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Guess a result list size. */
|
||||||
|
n = -1; /* unknown */
|
||||||
|
if (PySequence_Check(v) &&
|
||||||
|
v->ob_type->tp_as_sequence->sq_length) {
|
||||||
|
n = PySequence_Size(v);
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return NULL;
|
PyErr_Clear();
|
||||||
l = PyList_New(n);
|
}
|
||||||
if (l == NULL)
|
if (n < 0)
|
||||||
return NULL;
|
n = 8; /* arbitrary */
|
||||||
for (i = 0; ; i++) {
|
result = PyList_New(n);
|
||||||
PyObject *item = (*m->sq_item)(v, i);
|
if (result == NULL) {
|
||||||
if (item == NULL) {
|
Py_DECREF(it);
|
||||||
if (PyErr_ExceptionMatches(PyExc_IndexError))
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run iterator to exhaustion. */
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
PyObject *item = PyIter_Next(it);
|
||||||
|
if (item == NULL) {
|
||||||
|
/* We're out of here in any case, but if this is a
|
||||||
|
* StopIteration exception it's expected, but if
|
||||||
|
* any other kind of exception it's an error.
|
||||||
|
*/
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
if (PyErr_ExceptionMatches(PyExc_StopIteration))
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else {
|
else {
|
||||||
Py_DECREF(l);
|
Py_DECREF(result);
|
||||||
l = NULL;
|
result = NULL;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i < n)
|
|
||||||
PyList_SET_ITEM(l, i, item);
|
|
||||||
else if (PyList_Append(l, item) < 0) {
|
|
||||||
Py_DECREF(l);
|
|
||||||
l = NULL;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (i < n && l != NULL) {
|
if (i < n)
|
||||||
if (PyList_SetSlice(l, i, n, (PyObject *)NULL) != 0) {
|
PyList_SET_ITEM(result, i, item);
|
||||||
Py_DECREF(l);
|
else if (PyList_Append(result, item) < 0) {
|
||||||
l = NULL;
|
Py_DECREF(result);
|
||||||
}
|
result = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return l;
|
|
||||||
}
|
}
|
||||||
return type_error("list() argument must be a sequence");
|
|
||||||
|
/* Cut back result list if initial guess was too large. */
|
||||||
|
if (i < n && result != NULL) {
|
||||||
|
if (PyList_SetSlice(result, i, n, (PyObject *)NULL) != 0) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
result = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(it);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
Loading…
Reference in New Issue