]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Generalize list(seq) to work with iterators. This also generalizes list()
authorTim Peters <tim.peters@gmail.com>
Tue, 1 May 2001 20:45:31 +0000 (20:45 +0000)
committerTim Peters <tim.peters@gmail.com>
Tue, 1 May 2001 20:45:31 +0000 (20:45 +0000)
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
Misc/NEWS
Objects/abstract.c

index d4b8736293edd10e6995fa4a86fb9d0e58272a94..5b9bf652c441bef35d2fd7644659589a208719ed 100644 (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)
index 92882d2057fec9d2895e7401141fe9efbf6a2193..bce388928316326a713c394341860d82b9c53e01 100644 (file)
--- 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)?
 =================================
 
index f6567479f4221504c6a22b5d7c29106688dfef30..a5f97a18d0920e7fbf5460a931cb4b4f98829110 100644 (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 *