Учитывая следующие три функции
def v1(a):
c = 0
for a_ in a:
if a_ is not None:
c += 1
return c
def v2(a):
c = 0
for a_ in a:
if a_:
c += 1
return c
def v3(a):
c = 0
for a_ in a:
if bool(a_):
c += 1
return c
Я получаю следующую производительность (я использую python 3.6 на ubuntu 18.04)
values = [random.choice([1, None]) for _ in range(100000)]
%timeit v1(values)
3.35 ms ± 28 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit v2(values)
2.83 ms ± 36.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit v3(values)
12.3 ms ± 59.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Аналогичная производительность между v1 и v2 имеет смысл, но почему v3 намного медленнее, учитывая, что v2 предположительно также неявно вызывает bool(a_)?
Это просто вызов bool() из python, а не из c (как я предполагаю, if), что вызывает разницу в производительности?
(а как насчет c += bool(a)?)
Я считаю, что bool() фактически создаст экземпляр объекта класса bool, а затем выполнит проверку, которая приведет к снижению производительности.
bool() - это конструктор класса или типа, он общий и не оптимизирован для этой задачи, связанный: stackoverflow.com/questions/49009870/…Возможный дубликат Как проверить, пуст ли список?
@mbrig Это вовсе не дубликат только потому, что его затрагивает ответ.
@mbrig это совсем не тот вопрос. Связанный вопрос Chris_Rands намного ближе, но все же не дубликат.






Это в основном связано с динамизмом Python и тем фактом, что у вас есть вызов уровня Python.
Использование bool Python не может напрямую перейти и создать новый объект bool. Чтобы найти, что именно прикреплено к bool, необходимо выполнить поиск; затем он проверяет, можно ли его вызвать, анализирует его аргументы и
тогда назовите это.
Использование такой конструкции, как if _a, имеет определенное значение. Он проходит через определенный OPCODE (здесь POP_JUMP_IF_FALSE) и чеки, если загруженное значение имеет истинное значение. Намного меньше обручей, через которые нужно прыгнуть.
bool вызывает та же функция, чтобы проверить, является ли предоставленное значение True или False, у него просто более длительный путь, пока он не попадет туда.
v2 умеет оценивать «правдивость» a_в интерпретатора:
>>> dis.dis(v2)
...
11 14 LOAD_FAST 2 (a_)
16 POP_JUMP_IF_FALSE 10
...
где v3 требуется для фактического вызова bool на уровне Python:
>>> dis.dis(v3)
...
18 14 LOAD_GLOBAL 0 (bool)
16 LOAD_FAST 2 (a_)
18 CALL_FUNCTION 1
20 POP_JUMP_IF_FALSE 10
...
Вызов функции замедляет работу v3.
Потому что встроенные ... даже
xбыстрее, чемlen(x) != 0илиx != []. (в каком-то вопросе)