Что, как правило, быстрее, доходность или добавление?
В настоящее время я участвую в личном учебном проекте, где я читаю в базе данных XML. Я нахожу себя в написании функций, которые собирают данные, и я не уверен, что было бы быстрым способом вернуть их.
Что обычно быстрее:
-
yield
s, или
- несколько
append()
внутри функции, затем return
последующий list
?
Я был бы рад узнать, в каких ситуациях yield
будет быстрее, чем append()
или наоборот.
Ответы
Ответ 1
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
быстрее, чем append.
Ответ 2
Недавно я задал себе аналогичный вопрос, исследующий способы генерации всех перестановок списка (или кортежа) либо путем добавления в список, либо через генератор, и нашел (для перестановок длины 9, которые занимают около секунды или около того для генерации):
- Наивный подход (перестановки - это списки, добавление к списку, список возврата списков) занимает примерно в три раза больше времени
itertools.permutations
- Использование генератора (т.е.
yield
) уменьшает это на ок. 20%
- Использование генератора и генерации наборов является самым быстрым, примерно в два раза больше времени
itertools.permutations
.
Возьми с солью соль! Сроки и профилирование были очень полезными:
if __name__ == '__main__':
import cProfile
cProfile.run("main()")
Ответ 3
Существует еще более быстрая альтернатива TH4Ck yielding(). Это понимание списка.
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
Конечно, это глупо для микро-бенчмарка этих операций, не зная структуры вашего кода. Каждый из них полезен в разной ситуации. Например, понимание списков полезно, если вы хотите применить простую операцию, которая может быть выражена как одно выражение. Доходность имеет значительное преимущество для выделения кода обхода в метод генератора. Какой из них подходит, зависит от использования.
Ответ 4
В первую очередь u должен решить, если вам нужен генератор, это также улучшило метод. Как генератор списков "[elem для elem in somethink]". И генераторы должны быть рекомендованы, если вы просто используете значение в списке для некоторых операций. Но если вам нужен список для многих изменений и работать со многими элементами одновременно, это должен быть список.
(Как и 70%, если использовать стандартный список программистов, лучше будет генератор. Используйте меньше памяти, просто многие люди просто не видят другого способа списка. К сожалению, в нашу эпоху многие люди моют хорошую оптимуляцию и делают только работу.)
Если вы используете генератор для списка, чтобы улучшить возврат, сделайте то же самое с ребятами. В любом случае, мы получили несколько оптимизированных методов для всех действий на языке программирования 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]
Конечно, добавление будет медленнее, чем другие идеи, потому что мы создаем и расширяем список любого времени цикла. Просто цикл "for" очень неоптимизирован, если вы можете избежать этого, сделайте это. Becouse на любом этапе эта функция загружает следующий элемент и записывает нашу переменную, чтобы получить это значение объекта в памяти. Таким образом, мы переходим к любому элементу, создаем ссылку, расширяем список в цикле (объявленный метод является огромным оптическим регулятором скорости), когда мы генерируем только возврат, итоговое количество получило 2000 элементов в двух списках.
list_gen меньше по памяти, мы просто возвращаем элементы, но, как и вверх, мы генерируем список secound. Теперь у нас есть два списка, оригинальные данные и ее копия. Итоговые данные за 2000 элементов. Там мы просто избегаем шага с созданием ссылки на переменную. Becouse наш ген в списках избегает этого шага. Просто напишите элементы.
yielder использует меньше всего памяти, потому что мы только что получили значение из данных. Мы избегаем одной ссылки. Например:
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
Используйте только один элемент для какой-либо команды, а не все из списка, следующее одно значение yielder вернется в следующий цикл, а не журнал всех 1000 элементов для записи в ссылке.
Счастье для небольшой частичной отрывки, просто, когда я случайно попал в крест от поиска в Google, другие программисты-программисты на пионерах могут увидеть эту чушь.