Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 75f8e35

Browse files
committed
Generalize PySequence_Count() (operator.countOf) to work with iterators.
1 parent 1434299 commit 75f8e35

3 files changed

Lines changed: 70 additions & 15 deletions

File tree

Lib/test/test_iter.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,4 +527,39 @@ def test_in_and_not_in(self):
527527
except OSError:
528528
pass
529529

530+
# Test iterators with operator.countOf (PySequence_Count).
531+
def test_countOf(self):
532+
from operator import countOf
533+
self.assertEqual(countOf([1,2,2,3,2,5], 2), 3)
534+
self.assertEqual(countOf((1,2,2,3,2,5), 2), 3)
535+
self.assertEqual(countOf("122325", "2"), 3)
536+
self.assertEqual(countOf("122325", "6"), 0)
537+
538+
self.assertRaises(TypeError, countOf, 42, 1)
539+
self.assertRaises(TypeError, countOf, countOf, countOf)
540+
541+
d = {"one": 3, "two": 3, "three": 3, 1j: 2j}
542+
for k in d:
543+
self.assertEqual(countOf(d, k), 1)
544+
self.assertEqual(countOf(d.itervalues(), 3), 3)
545+
self.assertEqual(countOf(d.itervalues(), 2j), 1)
546+
self.assertEqual(countOf(d.itervalues(), 1j), 0)
547+
548+
f = open(TESTFN, "w")
549+
try:
550+
f.write("a\n" "b\n" "c\n" "b\n")
551+
finally:
552+
f.close()
553+
f = open(TESTFN, "r")
554+
try:
555+
for letter, count in ("a", 1), ("b", 2), ("c", 1), ("d", 0):
556+
f.seek(0, 0)
557+
self.assertEqual(countOf(f, letter + "\n"), count)
558+
finally:
559+
f.close()
560+
try:
561+
unlink(TESTFN)
562+
except OSError:
563+
pass
564+
530565
run_unittest(TestCase)

Misc/NEWS

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ Core
2323
max()
2424
min()
2525
reduce()
26+
tuple() (PySequence_Tuple() and PySequence_Fast() in C API)
2627
.join() method of strings
27-
tuple()
28+
'x in y' and 'x not in y' (PySequence_Contains() in C API)
29+
operator.countOf() (PySequence_Count() in C API)
2830
XXX TODO zip()
29-
'x in y' and 'x not in y'
31+
3032

3133
What's New in Python 2.1 (final)?
3234
=================================

Objects/abstract.c

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,34 +1333,52 @@ PySequence_Fast(PyObject *v, const char *m)
13331333
return v;
13341334
}
13351335

1336+
/* Return # of times o appears in s. */
13361337
int
13371338
PySequence_Count(PyObject *s, PyObject *o)
13381339
{
1339-
int l, i, n, cmp, err;
1340-
PyObject *item;
1340+
int n; /* running count of o hits */
1341+
PyObject *it; /* iter(s) */
13411342

13421343
if (s == NULL || o == NULL) {
13431344
null_error();
13441345
return -1;
13451346
}
1346-
1347-
l = PySequence_Size(s);
1348-
if (l < 0)
1347+
1348+
it = PyObject_GetIter(s);
1349+
if (it == NULL) {
1350+
type_error(".count() requires iterable argument");
13491351
return -1;
1352+
}
13501353

13511354
n = 0;
1352-
for (i = 0; i < l; i++) {
1353-
item = PySequence_GetItem(s, i);
1354-
if (item == NULL)
1355-
return -1;
1356-
err = PyObject_Cmp(item, o, &cmp);
1355+
for (;;) {
1356+
int cmp;
1357+
PyObject *item = PyIter_Next(it);
1358+
if (item == NULL) {
1359+
if (PyErr_Occurred())
1360+
goto Fail;
1361+
break;
1362+
}
1363+
cmp = PyObject_RichCompareBool(o, item, Py_EQ);
13571364
Py_DECREF(item);
1358-
if (err < 0)
1359-
return err;
1360-
if (cmp == 0)
1365+
if (cmp < 0)
1366+
goto Fail;
1367+
if (cmp > 0) {
1368+
if (n == INT_MAX) {
1369+
PyErr_SetString(PyExc_OverflowError,
1370+
"count exceeds C int size");
1371+
goto Fail;
1372+
}
13611373
n++;
1374+
}
13621375
}
1376+
Py_DECREF(it);
13631377
return n;
1378+
1379+
Fail:
1380+
Py_DECREF(it);
1381+
return -1;
13641382
}
13651383

13661384
/* Return -1 if error; 1 if v in w; 0 if v not in w. */

0 commit comments

Comments
 (0)