Насколько эффективен (в) эффективный список, если вы его не назначили?

В этом вопросе у меня есть аргумент с комментатором, который утверждает, что

for t in threads:
    t.join()

будет лучше, чем

[t.join() for t in threads]

Оставляя в стороне вопрос о "злоупотреблении пониманием" - я склонен согласиться, но я хотел бы сделать однострочный для этого: насколько эффективна моя версия (вторая)? Является ли Python материализовыванием списков всегда/в моем случае или он использует внутренний генератор?

Будет ли map(lambda t: t.join(), threads) более эффективным? Или есть другой способ применить функцию к каждому элементу в списке threads?

Ответ 1

Понимание списка всегда будет создавать объект списка, в этом случае с возвращаемыми значениями всех вызовов t.join(). Таким образом, Python создает для вас список с None значениями длины len(threads). Python никогда не попытается оптимизировать создание объекта списка.

Использование map() также не является более эффективным, поскольку вы добавляете дополнительные стоп-сигналы с помощью lambda. Просто придерживайтесь явного цикла for.

Действительно, для серии соединений нитей нет точки при попытке микро-оптимизации здесь. У вас болит читаемость для некритического кода.

Другими словами, я полностью согласен с комментатором. Не используйте понимание списка или map() только для побочных эффектов и сохраняйте себя, чтобы попасть в ENTER и создать две строки кода.

Цитирование Zen of Python:

  • Показатели удобочитаемости.

Ответ 2

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

def exhaust(iterator):
    for _ in iterator:
        pass

exhaust(t.join() for t in threads)

Затем вы не оплачиваете стоимость всего хранилища списка.

Невозможно переименовать exhaust что-то более быстрое или релевантное для вашего использования.

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

Ответ 3

Вы можете сделать foreach, который работает так, как вы хотите. Я бы рекомендовал против этого, так как это не обычный способ python делать вещи. Непроверенный, но по строкам:

class foreach:
    def __init__(self, object_list):
        self.__object_list = object_list

    def __getattr__(self, name):
        def f(*args, **kwargs):
            for obj in self.__object_list:
                getattr(obj, name)(*args, **kwargs)
        return f


foreach(thread_list).join()

Он в значительной степени создает своего рода прокси-объект, который пересылает любой вызов с любыми параметрами ко всем объектам.

(Скорее всего, у меня будет хороший чат, если вы это сделаете в моей команде разработчиков, но это служит примером того, что возможно в python)

Ответ 4

Если вам нужен однострочный шрифт, почему бы просто не сделать for t in threads: t.join()?

Похоже, это самое простое решение для меня и, вероятно, будет самым быстрым (хотя на самом деле накладные расходы на объединение потоков, скорее всего, будут затмевать что-нибудь еще IMO)