From f553f89d45f049e32f5eadc0c78a1e2440745d7a Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 1 May 2001 20:45:31 +0000 Subject: 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) --- Lib/test/test_iter.py | 32 +++++++++++++++++++ Misc/NEWS | 10 ++++++ Objects/abstract.c | 88 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 99 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index d4b8736..5b9bf65 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -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) diff --git a/Misc/NEWS b/Misc/NEWS index 92882d2..bce3889 100644 --- a/Misc/NEWS +++ b/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)? ================================= diff --git a/Objects/abstract.c b/Objects/abstract.c index f656747..a5f97a1 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -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 * -- cgit v0.12