a_list.count(a_callable) ?

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Sun Jun 17 03:04:55 CEST 2007

On Sat, 16 Jun 2007 20:37:01 +0000, Dustan wrote:

> On Jun 16, 12:04 pm, Wildemar Wildenburger <wilde... at freakmail.de>
> wrote:
>> class SmartCountingList(list):
>>     def count(self, item, func=lambda x: x):
>>         return len([item for item in self if func(item) is True])
> A less bug-prone and (I would think) speedier example, although still
> untested:
> class SmartCountingList(list):
>     def count(self, item, func=lambda x: x):
>         return sum(1 for i in self if func(item)==item)
> Then, you would call it as follows:
> a_list.count(True, a_function)

Did you intend for the method to count the number of items where
func(item) is item instead of true?

Personally, I don't see any advantage to making this a subclass. I think a
bare function would be far, far more sensible, since you could then apply
it to any sequence or iterable.

Here's a version that should work with any iterable. If the iterable is a
short enough sequence, it will use filter to build an intermediate list.
If it is too long, or if it can't predict how long the intermediate list
will be, it falls back to code that doesn't build an intermediate list.

(The cut-off length I have used is a wild guess. Somebody who cares more
than me can time the various strategies tactics and work out the "best"
length to switch from one strategy to another. Don't forget to try it in
different versions of Python.)

def count_items(iterable, func=None, _cutoff=100003):
    """Count the number of items of iterable where func(item) is a 
    true value. Equivalent to len(filter(func, seq)).

    If func is None or not given, counts the number of true items.

    Will not work for iterators that do not terminate.
        # Counting items in pure Python code is only 
        # worthwhile if building a temporary list using 
        # filter would take a long time.
        use_filter = len(iterable) < _cutoff
    except TypeError: 
        # iterable has no len(), so play it safe and 
        # avoid calling filter.
        use_filter = False
    if use_filter:
        # Take advantage of the fast filter built-in.
        return len(filter(func, iterable))
        n = 0
        if func is None:
            for item in iterable:
                if item: n += 1
            for item in iterable:
                if func(item): n += 1
        return n


More information about the Python-list mailing list