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

Ниже код, сравнивающий 10 способов: как из списка A удалить элементы, имеющиеся в списке B.

Всегда зависает (8-9 сек при 1000*100 элементов) на первом прохождении f_03 по списку, а в последующие разы f_03 показывает средненькие результаты без провала - Почему?

А если f_03 в repeat закомментировать (вообще не вызывать), то провал идет уже по f_04 - Есть идеи?

Закомментировать f_02 не помогает - все равно одна просадка в первом проходе f_03.

# Сравнение 10 способов: как из списка A удалить элементы, имеющиеся в списке B
import timeit

# заполним list размером с 'all' числами от 0 до 'different'
different = 1000

# длина списка исключений [0,1,2,3,4,5 ... 99]
deletion = 100

# есть список 100.000 элементов - (от 0 до 999)* 100 раз
a = list(range(different)) * 100

# будем из него удалять все элементы имеющиеся в этом списоке ( от 0 до 99 )
b = list(range(deletion))


def repeat(number):
    print("0123456789"+"-"*90)
    for i in range(number):
        result = dict()
        result['f_00'] = timer(f_00, a, b)
        print("+", end="")
        result['f_01'] = timer(f_01, a, b)
        print("+", end="")
        result['f_02'] = timer(f_02, a, b)
        print("+", end="")
        result['f_03'] = timer(f_03, a, b)
        print("+", end="")
        result['f_04'] = timer(f_04, a, b)
        print("+", end="")
        result['f_05'] = timer(f_05, a, b)
        print("+", end="")
        result['f_06'] = timer(f_06, a, b)
        print("+", end="")
        result['f_07'] = timer(f_07, a, b)
        print("+", end="")
        result['f_08'] = timer(f_08, a, b)
        print("+", end="")
        result['f_09'] = timer(f_09, a, b)
        print("+ : ", end="")
        print(result)
        # # Отсортируем по возрастанию
        # sorted_tuple = sorted(result.items(), key=lambda x: x[1])
        # print(sorted_tuple)
        # # преобразовываем обратно в словарь
        # result = dict(sorted_tuple)
    # print(f"result = {result.keys()}")


def f_00(a_, b_):
    result = []
    for number in a_:
        if number not in b_:
            result.append(number)
    return result


# 10 разных вариантов, как из списка 'a' удалить все элементы имеющиеся в списке 'b'
def f_01(a_, b_):
    return [x for x in a_ if x not in b_]


def f_02(a_, b_):
    return filter(lambda x: x not in b_, a_)


def f_03(a_, b_):  # Попа 100000 10 10 = 29-35сек, но потом все функции в разброс и тройка нормально действует
    for i in range(len(b_)):
        while b_[i] in a_:
            a_.remove(b_[i])
    return a_


def f_04(a_, b_):
    for x in b_:
        while x in a_:
            a_.remove(x)
    return a_


def f_05(a_, b_):
    for x in b_:
        while a_.count(x) != 0:
            a_.remove(x)
    return a_


def f_06(a_, b_):
    result = []
    for i in range(len(a_)):
        if a_[i] not in b_:
            result.append(a_[i])
    return result


def f_07(a_, b_):
    for i in range(len(b_)):
        g = 0
        while g != len(a_):
            if a_[g] == b_[i]:
                del a_[g]
                g -= 1
            g += 1
    return a_


def f_08(a_, b_):
    for x in b_:
        a_ = list(filter(lambda y: a_ != x, a_))
    return a_


def f_09(a_, b_):
    for x in a_:
        if x in b_:
            a_ = list(filter(lambda y: y != x, a_))
    return a_


def timer(func, list_1, list_2):
    # Функция замеряет время, требуемое для выполнения функции, переданной
    # в первом аргументе с параметрами для нее во втором и третьем аргументе
    start_time = timeit.default_timer()  # запустили таймер
    func(list_1, list_2)  # запускаем функцию перебора и удаления
    time_func = timeit.default_timer() - start_time  # сосчитали разницу - за сколько отработала функция
    # time = float('{:.9f}'.format(time))        # Нормально не преобразовывает
    return time_func


print("*" * 100, "\n", " " * 40, "Пошла родимая ...\n", "*" * 100)

repeat(10)  # Пройдемся всеми нашими функциями по исходному списку 'n' раз засекая время выполнения каждой функции

print("*" * 100, "\n", " " * 20, "Ну и терпение у тебя !!! Красавчик !!! Смотрим результаты;)\n", "*" * 100)
    


Ответы (1 шт):

Автор решения: GrAnd

Эм... Как бы любая первая функция с a_.remove() удаляет из списка a 10000 элементов. И последующие вызовы уже ничего не находят и не удаляют - вот и проходят быстрее. List же - mutable.

Вставьте print(len(a_)) в начало f_03() и сами всё увидите.

Надо перед каждым вызовом сделать копию списка, который будет подвержен изменению, и уже с ним запускать функции. Примерно вот так:

def timer(func, list_1, list_2):
    # Функция замеряет время, требуемое для выполнения функции, переданной
    # в первом аргументе с параметрами для нее во втором и третьем аргументе

    list_1_copy = list_1[:]   # <<< каждый раз делаем копию списка 
                              #     который будет модифицирован

    start_time = timeit.default_timer()  # запустили таймер
    func(list_1_copy, list_2)  # запускаем функцию перебора и удаления
    time_func = timeit.default_timer() - start_time  # сосчитали разницу - за сколько отработала функция
    return time_func

И ещё... Функцию repeat() можно оформить покороче, чтобы не было этих повторяющихся вызовов timer(). Да и параметр flush=True неплохо бы добавить в print() чтобы он плюсики сразу выводил.

def repeat(number):
    print("0123456789"+"-"*90)
    for i in range(number):
        result = dict()
        for n in range(10):
            func_name = f'f_{n:02d}'
            result[func_name] = timer(globals()[func_name], a, b)
            print("+", end="", flush=True)
        print([f"{f}: {v:7.4f}" for f, v in result.items()])

P.S. Терпения у меня нету, т.к. теперь уже все функции нормально так тормозят (ибо работают, а не пробегают мимо списка без нужных данных). Так что только 5 итераций:

0123456789------------------------------------------------------------------------------------------
++++++++++['f_00:  0.1206', 'f_01:  0.1193', 'f_02:  0.0000', 'f_03: 12.2089', 'f_04: 12.1900', 'f_05: 20.0656', 'f_06:  0.1255', 'f_07:  1.7437', 'f_08:  1.0205', 'f_09: 80.4800']
++++++++++['f_00:  0.1204', 'f_01:  0.1176', 'f_02:  0.0000', 'f_03: 12.2039', 'f_04: 12.1739', 'f_05: 20.0190', 'f_06:  0.1270', 'f_07:  1.7497', 'f_08:  1.0410', 'f_09: 81.4565']
++++++++++['f_00:  0.1198', 'f_01:  0.1174', 'f_02:  0.0000', 'f_03: 12.2061', 'f_04: 12.2196', 'f_05: 20.0409', 'f_06:  0.1256', 'f_07:  1.7130', 'f_08:  1.0475', 'f_09: 82.7945']
++++++++++['f_00:  0.1201', 'f_01:  0.1167', 'f_02:  0.0000', 'f_03: 12.1251', 'f_04: 12.3059', 'f_05: 20.2472', 'f_06:  0.1260', 'f_07:  1.7555', 'f_08:  1.0688', 'f_09: 84.0288']
++++++++++['f_00:  0.1193', 'f_01:  0.1164', 'f_02:  0.0000', 'f_03: 12.1219', 'f_04: 12.1641', 'f_05: 19.9973', 'f_06:  0.1256', 'f_07:  1.7269', 'f_08:  1.0684', 'f_09: 84.9647']
→ Ссылка