Что обычно быстрее, доходность или приложение? - PullRequest
15 голосов
/ 15 августа 2010

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

Что обычно быстрее:

  1. yield с или
  2. несколько append() с внутри функции, затем return последующие list?

Я был бы рад узнать, в каких ситуациях yield с будет быстреечем append() с или наоборот.

Ответы [ 4 ]

17 голосов
/ 15 августа 2010

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

# yield_vs_append.py
data = range(1000)

def yielding():
    def yielder():
        for d in data:
            yield d
    return list(yielder())

def appending():
    lst = []
    for d in data:
        lst.append(d)
    return lst

Это результат:

python2.7 -m timeit -s "from yield_vs_append import yielding,appending" "yielding()"
10000 loops, best of 3: 80.1 usec per loop

python2.7 -m timeit -s "from yield_vs_append import yielding,appending" "appending()"
10000 loops, best of 3: 130 usec per loop

По крайней мере, в этом очень простом тесте, yield быстрее, чем добавить.

8 голосов
/ 15 августа 2010

Недавно я задал себе аналогичный вопрос, исследуя способы генерации всех перестановок списка (или кортежа) либо путем добавления в список, либо с помощью генератора, и нашел (для перестановок длины 9, которые занимают около секунды или около того).для генерации):

  • Наивный подход (перестановки - списки, добавление к списку, возврат списка списков) занимает примерно три раза itertools.permutations
  • Использование генератора (т.е. yield) уменьшает это прибл.20%
  • Использование генератора и генерация кортежей - самое быстрое, примерно вдвое больше времени, чем itertools.permutations.

Принимайте с крошкой соли!Сроки и профилирование было очень полезно:

if __name__ == '__main__':
    import cProfile
    cProfile.run("main()")
6 голосов
/ 15 августа 2010

Существует еще более быстрая альтернатива уступке TH4Ck (). Это понимание списка.

In [245]: def list_comp():
   .....:     return [d for d in data]
   .....:

In [246]: timeit yielding()
10000 loops, best of 3: 89 us per loop

In [247]: timeit list_comp()
10000 loops, best of 3: 63.4 us per loop

Конечно, довольно глупо проводить микро-тестирование этих операций, не зная структуры вашего кода. Каждый из них полезен в разностной ситуации. Например, понимание списка полезно, если вы хотите применить простую операцию, которая может быть выражена как одно выражение. У выхода есть существенное преимущество для вас, чтобы изолировать код обхода в метод генератора. Какой из них подходит, во многом зависит от использования.

0 голосов
/ 20 июня 2017

Вначале вы должны решить, если вам нужен генератор, это также улучшенный метод. Как генератор списка "[elem for elem in somethink]". И генераторы рекомендуются, если вы просто используете значение в списке для некоторых операций. Но если вам нужен список для многих изменений и работа со многими элементами одновременно, это должен быть список. (Как и в 70% случаев, если стандартный программист использует список, лучше будет генератор. Используйте меньше памяти, просто многие люди просто не видят другого способа составления списка. К сожалению, в наше время многие люди сочетаются с хорошей оптимизацией и просто работают .)

Если вы используете генератор для списка, чтобы улучшить возврат, давайте сделаем то же самое с yield, ребята. Так или иначе, мы получили несколько более оптимизированных методов для всех действий в языке программирования Python.

Урожай быстрее, чем возврат, и я докажу это. Просто проверьте это, ребята:

data = range(1000)

def yielder():
    yield from data

def appending():
    L = []
    app = list.append
    for i in data:
        app(L, i)
    return L

def list_gen():
    return [i for i in data]

Конечно, добавление будет медленнее, чем другие идеи, потому что мы создаем и расширяем список в любое время цикла. Просто цикл «для» очень неоптимизирован, если вы можете избежать этого, сделайте это. На любом этапе эта функция загружает следующий элемент и записывает нашу переменную, чтобы получить значение этого объекта в памяти. Таким образом, мы переходим к любому элементу, создаем ссылку, расширяем список в цикле (объявленный метод является огромным быстродействующим оптимизатором), когда мы генерируем только return, итоговый результат получает 2000 элементов в двух списках.

list_gen меньше памяти, мы просто возвращаем элементы, но, как и вверх, мы генерируем второй список. Теперь у нас есть два списка, оригинальные данные и ее копия. Резюме 2000 элементов. Там просто избегаем шага с созданием ссылки на переменную. Поскольку наш ген в списках избегает этого шага. Просто напишите элементы.

получатель использует меньше всего памяти, потому что мы получили только что полученное значение из данных. Мы избегаем одной ссылки. Например:

data = range(1000)

def yielder():
    yield from data

def list_gen():
    return [i for i in data]

#Now we generate next reference after line [i for i in data]
for i in list_gen():
    #some instruction

#This is our first reference, becouse was yield from data.
for i in yielder():
    #some instruction

Используйте только один элемент для какой-то инструкции, а не все из списка, следующее значение возвращает возвращаемое значение в следующем цикле, а не записывает все 1000 элементов для записи в ссылку.

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

...