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:
|
||||
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)
|
||||
|
|
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)?
|
||||
=================================
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
Loading…
Reference in New Issue