Комментарии 71
НЛО прилетело и опубликовало эту надпись здесь
Подсчет элементов в словаре
…
Отлично: само продвинутый способ это использовать defaultdict().
А как же Counter?
У izip и подобных (а также, у zip из 3 питона) есть очень неприятная особенность: по созданным таким образом объектам нельзя два раза пройтись, нужно заново создавать. Иногда хотелось бы, чтобы в таких вещах было как в хаскеле :)
Можно, и заодно потерять в скорости по сравнению с обычным zip. Причём в 3 питоне обычный встроенный zip делает итератор, а не список, поэтому в случае возможной необходимости многократной итерации нужно не забывать писать list(zip(...)). Если этого не сделать, то никакого исключения не будет, просто второй раз будет 0 итераций.
Здесь где‐то было сравнение скорости, Counter сильно отстаёт. Если я не ошибаюсь, Counter написан на Python, defaultdict — на C.
Немного не понял, чем отличается проход по списку задом наперед (в стиле С) от прямого прохода?
Возможно, имелось в виду:
или даже более привычный для сишника синтаксис:
Возможно, имелось в виду:
print colors[-i]
или даже более привычный для сишника синтаксис:
print colors[len(colors)-i]
Возможно, имелось в виду:
print colors[-i]
print colors[-i - 1]
или даже более привычный для сишника синтаксис:
print colors[len(colors)-i]
Плохая привычка — вызывать функцию с одним и тем же аргументом на каждой итераци :)
Дико извиняюсь, имелось в виду:
Исправил.
for i in range(len(colors)-1, -1, -1):
print colors[i]
Исправил.
Спасибо за статью :) Все это уже знал, но все ровно приятно перечитывать подобные вещи снова и снова :)
> в отличии от zip, izip использует кэширование, что помогает существенно сэкономить память.
И что именно кэширует izip?
И что именно кэширует izip?
Стоит упомянуть, что iteritems тоже в Python 3 убрали.
Если это перевод, можно ссылку на оригинал?
Ваше объяснение разницы между range и xrange, zip и izip какое-то кривое.
Если это перевод, можно ссылку на оригинал?
Ваше объяснение разницы между range и xrange, zip и izip какое-то кривое.
Наверное, стоит добавить, что кеширующая über-функция izip лежит в модуле itertools.
Сам модуль itertools, кстати — сокровищница.
Сам модуль itertools, кстати — сокровищница.
Группирование элементов списка
А еще лучше помнить про библиотечные функции c:
itertools.groupby(names, len)
О, спасибо большое! Всегда использовал range(len(list)), если нужно было пройтись не по элементам, а по индексам. Буду отучать себя потихоньку =)
Посдчёт элементов в стловаре
Отлично: само продвинутый способ это использовать defaultdict(). Но вы должны знать как он работает.
d = defaultdict(int) for color in colors: d[color] += 1
Эээм.
>>> d = {'test': 123, 'one':321}
>>> d
{'test': 123, 'one': 321}
>>> len(d)
2
Вы бы вычитали статью, ошибок куча.
«метод cmp убран с ядра Python 3.x», «длинна строки», «делаем с двух списков один список кортежей» и так далее.
«метод cmp убран с ядра Python 3.x», «длинна строки», «делаем с двух списков один список кортежей» и так далее.
В личку, пожалуйста.
Перечитаю еще раз.
Перечитаю еще раз.
Если бы тут просто было навыделять ошибок и отправить в личку — я так и делал, а искать, собирать куда-то, потом писать в виде списка в личку — увольте. Я заколебался этим заниматься, если честно.
Очень скоро в резюме в графе «владение языками» можно будет писать русский язык.
Статью надо бы начинать:
Плохо: иногда пишут так.
Хорошо:
Плохо: иногда пишут так.
называеться
Хорошо:
называется
Подсчет элементов в словаре
Если список colors ооочень большой, то самый быстрый способ будет сделать так:
for color in colors:
try:
d[color] += 1
except KeyError:
d[color] = 1
можно короче
for color in colors:
d.setdefault(color, 0)
d[color] += 1
Готовим коллекцию
Запускаем код A:
Запускаем код B:
Ваш код на ~30% короче и на ~20% медленнее (на моем локальном компьютере).
import random
a = [random.randint(1, 10000) for a in range(10000000)]
Запускаем код A:
d = {}
for i in a:
try:
d[i] += 1
except KeyError:
d[i] = 1
Запускаем код B:
d = {}
for i in a:
d[i].setdefault(i, 0)
d[i] += 1
Ваш код на ~30% короче и на ~20% медленнее (на моем локальном компьютере).
А где сравнение с defaultdict?
У меня defaultdict самый быстрый:
from timeit import timeit
import random
from collections import defaultdict
a = [random.randint(1, 10000) for a in range(10000000)]
def dictionary_setdefault(l, d={}):
for i in l:
d.setdefault(i, 0)
d[i] += 1
def dictionary_keyerror(l, d={}):
for i in l:
try:
d[i] += 1
except KeyError:
d[i] = 1
def defaultdict_simple(l, d=defaultdict(int)):
for i in l:
d[i] += 1
for func in (dictionary_setdefault, dictionary_keyerror, defaultdict_simple):
import sys
sys.stderr.write('Processing {0}\n'.format(func))
print (timeit('import __main__; __main__.{0}(__main__.a)'.format(func.__name__), number=1))
У меня defaultdict самый быстрый:
Processing <function dictionary_setdefault at 0x7f16a635c758>
4.41468501091
Processing <function dictionary_keyerror at 0x7f16a635ced8>
2.28980493546
Processing <function defaultdict_simple at 0x7f16a635cf50>
2.04581904411
(Python 2.7.5). Python 3.2.5:4.152358055114746
2.3733527660369873
1.9591529369354248
Processing <function dictionary_setdefault at 0x7ff5409bda68>
Processing <function dictionary_keyerror at 0x7ff5409bd9e0>
Processing <function defaultdict_simple at 0x7ff5409bd518>
(по всей видимости, stderr буферизуется не по строкам).Хм. Достаточно интересный результат. Сравнение keyerror vs defaultdict я проводил на реальных данных в одном из проектов, и там, скорей всего в силу специфики данных, keyerror оказался быстрее.
Но как видно из вашего синтетического теста, лучше по-умолчанию использовать default_dict.
А в Python 3.2 вообще нет смысла использовать для данных целей решение отличное от defaultdict :)
Но как видно из вашего синтетического теста, лучше по-умолчанию использовать default_dict.
А в Python 3.2 вообще нет смысла использовать для данных целей решение отличное от defaultdict :)
На картинке судя по всему Falcon атакует Python =)
В оригинальном видео еще много о чем рассказывается: о хорошем использовании контекстных менеджеров, о генераторах, о deque. Мне кажется вы несправедливо умолчали об этом.
Последнюю задачу можно решить без использования collections.defaultdict, а также короче получается:
d = {}
for name in names:
d.setdefault(len(name), []).append(name)
Ещё один (анти)паттерн, который здесь несколько раз промелькнул, но не был подчёркнут:
Т.е. когда нужны не только сами элементы, но и их индексы, стоит использовать генератор кортежей (индекс, элемент).
Причём эта штука работает и в генераторных выражениях (list/set/dict/generator comprehension):
xs = ['zero', 'one', 'two', 'three', 'four']
# плохо
for i in xrange(len(xs)) :
print i, '-->', xs[i]
# ужасно
i = 0
for x in xs :
print i, '-->', xs[i]
i += 1
# хорошо и даже отлично
for i,x in enumerate(xs) :
print i, '-->', xs[i]
Т.е. когда нужны не только сами элементы, но и их индексы, стоит использовать генератор кортежей (индекс, элемент).
Причём эта штука работает и в генераторных выражениях (list/set/dict/generator comprehension):
show = ['xs[%d]=%s' for i,x in enumerate(xs) if i%2==0] # xs[0]=zero, xs[2]=two, xs[4]=four
look = {x:i for i,x in enumerate(xs)} # для поиска индекса по значению
Чем такое:
Лучше такого:
?
Просто интересно.
colors = ['red', 'green', 'blue', 'yellow']
for color in reversed(colors):
print color
Лучше такого:
colors = ['red', 'green', 'blue', 'yellow']
for color in colors[::-1]:
print color
?
Просто интересно.
Более «питонический» код, по крайней мере так говори core разработчик и я ему верю.
www.python.org/dev/peps/pep-0020/
The Zen of Python. Первые строки
The Zen of Python. Первые строки
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated..
Окей, тогда другой вопрос — что является превалирующим: субъективное понятие красоты или более-менее объективная простота чтения? :)
reversed делает со списком только одну вещь, а [start, end, step] — универсальный способ, значит, работает медленнее.
Ради интереса проверил ваши слова, получилось, что оба варианта работают с одинаковой скоростью, как 100000 прогонов по 4 элемента в списке, так и 1 прогон по 100000 элементов в списке. Во всяком случае в третьем питоне.
Мне кажется, это такой умный интерпретатор.
P.S. Вот всё и объяснилось: habrahabr.ru/post/204476/#comment_7055038
P.S. Вот всё и объяснилось: habrahabr.ru/post/204476/#comment_7055038
Слайс снимает копию со списка, а reversed возвращает итератор, проходящий по имеющемуся списку в обратном направлении.
А почему вдруг
медленнее, чем
Мало того, что первый вариант быстрее, так он еще и не создает в памяти список для итерации:
for k in d: k, d[k]
медленнее, чем
for k, v in d.items(): k, v
Мало того, что первый вариант быстрее, так он еще и не создает в памяти список для итерации:
%timeit for k in d: k, d[k] 1000 loops, best of 3: 1.33 ms per loop %timeit for k, v in d.items(): k, d 1000 loops, best of 3: 1.92 ms per loop
Здесь дело в том что итерируя d вы не можете его изменять.
Второй же вариант, как вы правильно написали, создает список и использует его в качестве итератора, что позволяет спокойно работать с d.
Второй же вариант, как вы правильно написали, создает список и использует его в качестве итератора, что позволяет спокойно работать с d.
Быстрее и не создает в памяти список:
d = {i:i for i in xrange(40 * 1000 * 1000)}
timeit for k in d: k, d[k]
1 loops, best of 3: 2.63 s per loop
timeit for k, v in d.iteritems(): k, d
1 loops, best of 3: 2.38 s per loop
d = {i:i for i in xrange(40 * 1000 * 1000)}
timeit for k in d: k, d[k]
1 loops, best of 3: 2.63 s per loop
timeit for k, v in d.iteritems(): k, d
1 loops, best of 3: 2.38 s per loop
В Python 3 iteritems переименовали в items. Так что для совместимости, если скорость не критична, писать items. И второй вариант также не будет создавать список — пока вы используете Python 3.
Мое ИМХО пусть лучше будет С-style код но с четкой и понятной архитектурой, чем непонятная лапша из «красивого идиоматического Python» кода. К сожалению наблюдается обычно обратная ситуация и вся мощь и гибкость языка используется для того чтобы максимально запутать читателя такого кода.
Можно услышать обоснованную критику?
Язык С — не образец четкости и понятности. Он гораздо ближе к тому, как работает CPU, чем к тому, как работает человеческий мозг.
Если «идиоматические» конструкции в примере кажутся «непонятной лапшой», так это потому что большинство программистов училось писать на С-подобных языках и привыкло писать на С-подобных языках.
Собственно, в большинстве примеров и описываются конструкции, которые пишут люди, привыкшие к С, но очень поверхностно освоившие Python. Человек, который сразу изучал Python, возможно, даже не сразу поймет, что хотели сказать конструкцией
(Это не в коем случае не холивар, я на обоих языках пишу, и оба мне нравятся. Просто, если они разные, это еще не значит, что Python плохой)
Если «идиоматические» конструкции в примере кажутся «непонятной лапшой», так это потому что большинство программистов училось писать на С-подобных языках и привыкло писать на С-подобных языках.
Собственно, в большинстве примеров и описываются конструкции, которые пишут люди, привыкшие к С, но очень поверхностно освоившие Python. Человек, который сразу изучал Python, возможно, даже не сразу поймет, что хотели сказать конструкцией
for i in range(len(colors)):
print colors[i]
(Это не в коем случае не холивар, я на обоих языках пишу, и оба мне нравятся. Просто, если они разные, это еще не значит, что Python плохой)
Боюсь Вы не правильно поняли мой комментарий. Я сам пишу на обоих языках и люблю оба.
Я имел ввиду что питон (за счет лаконичности) способствует перегрузке функций излишней ответственностью.
Функция должна делать ровно 1 вещь и делать ее хорошо, а в питоне зачастую можно увидеть функцию длиной 40-50 строк которая тем не менее делает 100500 разных вещей которой слабо связаны между собой. Именно это я и пытался донести в своем комментарии.
Вопрос не в том как будет записан цикл по списку, а в том насколько такой код обладает четкой и понятной архитектурой и отвечает принципам SOLID.
Я имел ввиду что питон (за счет лаконичности) способствует перегрузке функций излишней ответственностью.
Функция должна делать ровно 1 вещь и делать ее хорошо, а в питоне зачастую можно увидеть функцию длиной 40-50 строк которая тем не менее делает 100500 разных вещей которой слабо связаны между собой. Именно это я и пытался донести в своем комментарии.
Вопрос не в том как будет записан цикл по списку, а в том насколько такой код обладает четкой и понятной архитектурой и отвечает принципам SOLID.
Функция должна делать ровно 1 вещь и делать ее хорошо, а в питоне зачастую можно увидеть функцию длиной 40-50 строк которая тем не менее делает 100500 разных вещей которой слабо связаны между собойЧестно, не очень понимаю, причем тут язык. Даже не представляю, какие особенности Питона могут вынудить нарушать этот принцип.
Если дан, например, массив чисел и надо вывести только те, которые больше 10, а предидущий элемент меньше в два раза. На C++ это будет как-то так
Можно ли это сделать на на Python без двух итераторов или индексов по массиву?
for (auto j = array.begin(), J=array.end(); j!=J; ++j)
if (*j>10)
if (*j == *(j--)*2)
std::cout<<*j<<'\n';
Можно ли это сделать на на Python без двух итераторов или индексов по массиву?
Там ++(array.begin()) конечно же.
Можно сделать так:
[x[1] for x in zip(array, array[1:]) if x[1] == x[0] * 2 and x[1] > 10]
Но здесь, конечно, двух итераторов не будет только внешне.Во-первых, у вас глюк — нужно (j-1), а с j-- вы так и будете ходить по этому элементу бесконечно туда-сюда. Плюс вылет за границу, если первый элемент больше десяти. Плюс, кажется, undefined behavior из-за неопределенности порядка выполнения j-- и *j.
Вообще же, все задачи вида "… а предыдущий элемент ..." в языках с zip решаются зипованием последовательности с собой же со сдвигом. Тупо (и медленно, потому что с копированием):
Или быстрее с islice, который не копирует, а создает ленивый итератор со сдвигом:
Вообще же, все задачи вида "… а предыдущий элемент ..." в языках с zip решаются зипованием последовательности с собой же со сдвигом. Тупо (и медленно, потому что с копированием):
[x for x_prev, x in zip(xs, xs[1:]) if x > 10 and x == x_prev * 2]
Или быстрее с islice, который не копирует, а создает ленивый итератор со сдвигом:
[x for x_prev, x in zip(xs, islice(xs, 1, None)) if x > 10 and x == x_prev * 2]
j-- создаёт новый объект, так что всё нормально undefined нет. На счёт вылета я там поправил уже.
С islice хорошо да. Но опять же у вас два итератора всегда а не только когда нужно.
С islice хорошо да. Но опять же у вас два итератора всегда а не только когда нужно.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Пишем красивый идиоматический Python