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:
Tim Peters 2001-05-01 20:45:31 +00:00
parent 47668928e6
commit f553f89d45
3 changed files with 99 additions and 31 deletions

View File

@ -243,4 +243,36 @@ class TestCase(unittest.TestCase):
except OSError:
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)

View File

@ -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)?
=================================

View File

@ -1236,52 +1236,78 @@ PySequence_Tuple(PyObject *v)
PyObject *
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)
return null_error();
/* Special-case list(a_list), for speed. */
if (PyList_Check(v))
return PyList_GetSlice(v, 0, PyList_GET_SIZE(v));
m = v->ob_type->tp_as_sequence;
if (m && m->sq_item) {
int i;
PyObject *l;
int n = PySequence_Size(v);
/* Get iterator. There may be some low-level efficiency to be gained
* by caching the tp_iternext slot instead of using PyIter_Next()
* later, but premature optimization is the root etc.
*/
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)
return NULL;
l = PyList_New(n);
if (l == NULL)
return NULL;
for (i = 0; ; i++) {
PyObject *item = (*m->sq_item)(v, i);
if (item == NULL) {
if (PyErr_ExceptionMatches(PyExc_IndexError))
PyErr_Clear();
}
if (n < 0)
n = 8; /* arbitrary */
result = PyList_New(n);
if (result == NULL) {
Py_DECREF(it);
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();
else {
Py_DECREF(l);
l = NULL;
Py_DECREF(result);
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 (PyList_SetSlice(l, i, n, (PyObject *)NULL) != 0) {
Py_DECREF(l);
l = NULL;
}
if (i < n)
PyList_SET_ITEM(result, i, item);
else if (PyList_Append(result, item) < 0) {
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 *