summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-05-05 11:33:43 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-05-05 11:33:43 (GMT)
commit75f8e35ef41aa6e7c915d99de8bd40be2745955c (patch)
treecbc72fb0979618f4b31f151d73d7b3bf775fd263
parent1434299a991edcb3a2bf604dc139719b2cca5490 (diff)
downloadcpython-75f8e35ef41aa6e7c915d99de8bd40be2745955c.zip
cpython-75f8e35ef41aa6e7c915d99de8bd40be2745955c.tar.gz
cpython-75f8e35ef41aa6e7c915d99de8bd40be2745955c.tar.bz2
Generalize PySequence_Count() (operator.countOf) to work with iterators.
-rw-r--r--Lib/test/test_iter.py35
-rw-r--r--Misc/NEWS6
-rw-r--r--Objects/abstract.c44
3 files changed, 70 insertions, 15 deletions
diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py
index bb9b102..7d15e1c 100644
--- a/Lib/test/test_iter.py
+++ b/Lib/test/test_iter.py
@@ -527,4 +527,39 @@ class TestCase(unittest.TestCase):
except OSError:
pass
+ # Test iterators with operator.countOf (PySequence_Count).
+ def test_countOf(self):
+ from operator import countOf
+ self.assertEqual(countOf([1,2,2,3,2,5], 2), 3)
+ self.assertEqual(countOf((1,2,2,3,2,5), 2), 3)
+ self.assertEqual(countOf("122325", "2"), 3)
+ self.assertEqual(countOf("122325", "6"), 0)
+
+ self.assertRaises(TypeError, countOf, 42, 1)
+ self.assertRaises(TypeError, countOf, countOf, countOf)
+
+ d = {"one": 3, "two": 3, "three": 3, 1j: 2j}
+ for k in d:
+ self.assertEqual(countOf(d, k), 1)
+ self.assertEqual(countOf(d.itervalues(), 3), 3)
+ self.assertEqual(countOf(d.itervalues(), 2j), 1)
+ self.assertEqual(countOf(d.itervalues(), 1j), 0)
+
+ f = open(TESTFN, "w")
+ try:
+ f.write("a\n" "b\n" "c\n" "b\n")
+ finally:
+ f.close()
+ f = open(TESTFN, "r")
+ try:
+ for letter, count in ("a", 1), ("b", 2), ("c", 1), ("d", 0):
+ f.seek(0, 0)
+ self.assertEqual(countOf(f, letter + "\n"), count)
+ finally:
+ f.close()
+ try:
+ unlink(TESTFN)
+ except OSError:
+ pass
+
run_unittest(TestCase)
diff --git a/Misc/NEWS b/Misc/NEWS
index 468eae6..aecc5e9 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -23,10 +23,12 @@ Core
max()
min()
reduce()
+ tuple() (PySequence_Tuple() and PySequence_Fast() in C API)
.join() method of strings
- tuple()
+ 'x in y' and 'x not in y' (PySequence_Contains() in C API)
+ operator.countOf() (PySequence_Count() in C API)
XXX TODO zip()
- 'x in y' and 'x not in y'
+
What's New in Python 2.1 (final)?
=================================
diff --git a/Objects/abstract.c b/Objects/abstract.c
index a0a40e8..21c1ef1 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -1333,34 +1333,52 @@ PySequence_Fast(PyObject *v, const char *m)
return v;
}
+/* Return # of times o appears in s. */
int
PySequence_Count(PyObject *s, PyObject *o)
{
- int l, i, n, cmp, err;
- PyObject *item;
+ int n; /* running count of o hits */
+ PyObject *it; /* iter(s) */
if (s == NULL || o == NULL) {
null_error();
return -1;
}
-
- l = PySequence_Size(s);
- if (l < 0)
+
+ it = PyObject_GetIter(s);
+ if (it == NULL) {
+ type_error(".count() requires iterable argument");
return -1;
+ }
n = 0;
- for (i = 0; i < l; i++) {
- item = PySequence_GetItem(s, i);
- if (item == NULL)
- return -1;
- err = PyObject_Cmp(item, o, &cmp);
+ for (;;) {
+ int cmp;
+ PyObject *item = PyIter_Next(it);
+ if (item == NULL) {
+ if (PyErr_Occurred())
+ goto Fail;
+ break;
+ }
+ cmp = PyObject_RichCompareBool(o, item, Py_EQ);
Py_DECREF(item);
- if (err < 0)
- return err;
- if (cmp == 0)
+ if (cmp < 0)
+ goto Fail;
+ if (cmp > 0) {
+ if (n == INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "count exceeds C int size");
+ goto Fail;
+ }
n++;
+ }
}
+ Py_DECREF(it);
return n;
+
+Fail:
+ Py_DECREF(it);
+ return -1;
}
/* Return -1 if error; 1 if v in w; 0 if v not in w. */