Подтвердить что ты не робот

Условный подсчет в Python

Не уверен, что это было задано раньше, но я не мог найти очевидного ответа. Я пытаюсь подсчитать количество элементов в списке, равное определенному значению. Проблема в том, что эти элементы не имеют встроенного типа. Поэтому, если у меня есть

class A:
    def __init__(self, a, b):
        self.a = a
        self.b = b

stuff = []
for i in range(1,10):
    stuff.append(A(i/2, i%2))

Теперь мне нужно подсчитать элементы списка, поле b = 1. Я придумал два решения:

print [e.b for e in stuff].count(1)

и

print len([e for e in stuff if e.b == 1])

Какой лучший метод? Есть ли лучшая альтернатива? Похоже, что метод count() не принимает ключи (по крайней мере, в версии 2.5.1 Python.

Большое спасибо!

4b9b3361

Ответ 1

sum(x.b == 1 for x in L)

Логическое значение (как результат сравнения, например x.b == 1) также равно int со значением 0 для False, 1 для True, поэтому арифметика, такая как суммирование, работает просто хорошо.

Это простейший код, но, возможно, не самый быстрый (только timeit может вам точно сказать;-). Рассмотрим (упрощенный случай, чтобы хорошо вписываться в командные строки, но эквивалентно):

$ py26 -mtimeit -s'L=[1,2,1,3,1]*100' 'len([x for x in L if x==1])'
10000 loops, best of 3: 56.6 usec per loop
$ py26 -mtimeit -s'L=[1,2,1,3,1]*100' 'sum(x==1 for x in L)'
10000 loops, best of 3: 87.7 usec per loop

Итак, для этого случая подход "потеря памяти" для создания дополнительного временного списка и проверки его длины на самом деле прочно быстрее, чем более простая, более короткая, экономящая память, которую я предпочитаю. Разумеется, другие сочетания значений списка, реализации Python, доступность памяти для "инвестиций" в это ускорение и т.д. Могут влиять на точную производительность.

Ответ 2

print sum(1 for e in L if e.b == 1)

Ответ 3

Я предпочел бы второй, поскольку он только циклически перебирает список.

Если вы используете count(), вы перебираете список один раз, чтобы получить значения b, а затем снова перебираете его, чтобы увидеть, сколько из них равно 1.

В аккуратном способе можно использовать reduce():

reduce(lambda x,y: x + (1 if y.b == 1 else 0),list,0)

Документация сообщает нам, что reduce() будет:

Применить функцию двух аргументов кумулятивно к элементам итерации слева направо, чтобы уменьшить итерацию до одного значения.

Итак, мы определяем a lambda, который добавляет одно накопленное значение, только если атрибут b элемента списка равен 1.

Ответ 4

Чтобы скрыть детали reduce, вы можете определить функцию count:

def count(condition, stuff):
    return reduce(lambda s, x: \
                  s + (1 if condition(x) else 0), stuff, 0)

Затем вы можете использовать его, указав условие для подсчета:

n = count(lambda i: i.b, stuff)