links = ['http://foo.com/page1', 'http://foo.com/page2', 'http://foo.com/page3', 'http://foo.com/', 'http://blah.org/file0', 'http://blah.org/file1', 'http://foobar.net/some_page', 'http://foobar.net/another_page', 'http://foobar.net/yet_another', 'http://blah.org/file2', 'http://blah.org/file3', 'http://blah.org/file4'] Надо скачать страницы/файлы по этим ссылкам, но не создавая избыточную нагрузку на каждый из хостов, чтобы, так сказать, пройти ниже радара.* Можно ввести паузы между закачками, но это сильно затянет исполнение. Самое простое решение -- отсортировать список так, чтобы хосты чередовались между собой, и последовательные запросы гарантировано приходились на разные хосты. Но как выполнить такую сортировку, не прибегая к громоздким циклам? На помощь приходит модуль itertools. Для начала определим критерий сортировки: нас интересует имя хоста: from urllib.parse import urlparse sort_by_host = lambda url: urlparse(url).netloc Ну, а теперь самое весёлое. Этот код можно легко переписать в однострочник, но, памятуя, что from itertools import groupby, zip_longest # Сортируем по именам хостов. sorted_urls = sorted(links, key=sort_by_host) # Группируем хосты. grouped = [list(group) for _, group in groupby(sorted_urls, key=sort_by_host)] # Сортируем хосты циклически. interleaved = list(zip_longest(*grouped)) # Преобразуем в однородный список без None-элементов. output = [item for group in interleaved for item in group if item is not None] # Полученный результат. for link in output: print(link) # http://blah.org/file0 # http://foo.com/page1 # http://foobar.net/some_page # http://blah.org/file1 # http://foo.com/page2 # http://foobar.net/another_page # http://blah.org/file2 # http://foo.com/page3 # http://foobar.net/yet_another # http://blah.org/file3 # http://foo.com/ # http://blah.org/file4 Для ценителей лаконичности та же функция в одну строку. Ну, или уж как получится. :) output = [i for e in list(zip_longest(*[list(g) for _, g in groupby(sorted(links, key=sort_by_host), key=sort_by_host)])) for i in e if i is not None] * Реальная задача, из которой родился пост, была связана с API некоторого сайта, лимитирующим плотность вызовов, но детали той задачи не столь иллюстративны в рамках краткой заметки. |