Pull to refresh

Comments 24

Мне вместо map намного удобнее использовать list generator да и читается оно легче.
Хочу заметить, что списковые выражения совмещают в себе и map, и filter, ещё и быстрее работают.

А ещё хочу сказать автору, что это 3 базовые функции для работы с последовательностями в любом языке. Не могу представить, как их можно не знать.
UFO just landed and posted this here
«повышения скорости» не будет вообще. функции в питоне дороги, и тот же цикл, записанный имплицитно, будет работать в разы быстрее. увы.

Ну во-первых, далеко не в разы, а всего лишь процентов на 5. Во-вторых — list comprehension работает еще быстрее. Но тоже далеко не в разы, а процентов на 10 быстрее цикла (И соответственно, процентов на 15 быстрее map).


Пример на моей машине
In [10]: l = list(range(1000000))

In [11]: %timeit test_map(l)
346 ms ± 3.94 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [12]: %timeit test_loop(l)
328 ms ± 802 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [13]: %timeit test_list_comprehension(l)
292 ms ± 970 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [14]: %timeit  test_generator(l)
313 ms ± 2.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [15]: %timeit test_loop2(l)
324 ms ± 411 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

Сами тесты
In [1]: def test_map(l):
   ...:     return list(map(lambda x: x ** 2, l))
   ...:

In [2]: def test_loop(l):
   ...:     res = []
   ...:     for n in l:
   ...:         res.append(n ** 2)
   ...:     return res
   ...:

In [3]: def test_list_comprehension(l):
   ...:     return [n**2 for n in l]
   ...:

In [4]: def test_generator(l):
   ...:     return list((n**2 for n in l))
   ...:

In [5]: def test_loop2(l):
    ...:     res = [0] * len(l)
    ...:     for i, n in enumerate(l):
    ...:         res[i] = n ** 2
    ...:     return res
    ...:

У вас тесты делают разные вещи. Reduce и any_lst пробегают весь список, тогда как в остальных двух тестах есть ранняя остановка.


Так что объективно с циклом можно сравнивать только any_gen. И да в данном случае он работает в ~2 раза медленнее.
Но учитывайте, что оба варианта (и список и генератор) здесь делают в среднем всего лишь ок. 50 итераций на тест. Поэтому накладные расходы на инициализацию генераторов заметно сказываются на времени выполнения. Если проверять на более длинных списках, то никаких "в разы быстрее" уже не останется.

а, да. действительно, я эти тесты делал для иллюстрирования немного другой идеи.
ну хорошо, давайте сделаем такой цикл, а генератор забудем как нерелевантный:

def loop(error, list_of_errors):
    a = False
    for error_message in list_of_errors:
        if error in error_message:
            a = True
    return a


list items 10, list length 500:
  • loop 5.5052784
  • reduce 12.4489548
  • any_lst 9.775371399999997


list items 5000, list length 10:
  • loop 40.1854446
  • reduce 53.282886000000005
  • any_lst 56.423127699999995


всё равно ощутимо медленнее. оверхед на вызов функции даёт о себе знать.

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

Я не уверен, что цепочки filter, map, reduce так уж красивы и выразительны в Python. Все же синтаксис Python, изначально императивного языка, активно сопротивляется подобному. Например lambda функции, которые часто используют в фильтрах и мапах, в Python сделаны слишком уж многословными. Или отсутствие в языке встроеного метода композиции. Иной раз приходится на ревью кода плакать кровавыми слезами от попыток в лоб сделать многоступенчатую обработку с кучей лямбд.

Во всём меру знать нужно, конечно. Например, за такой comprehension golf можно и селёдкой по щщам:

{
    'pages': [
        {
            'customerapprovalStatus'
                if k == 'statusCA' else k:
            'APPROVED'
                if k == 'statusCA' else page[k]
            for k in page if k == 'pageID' or k == 'statusCA'
        }
        for page in elements['pages']
    ]
}


А для построения IMAP-запроса с множественными FROM reduce подходит как нельзя лучше: gist.github.com/zmej-serow/4d6292591c690563e7dd205964f8b8ee
Вряд ли кто не слышал про эти функции. Да и это не самые «питонячие» вещи.
map и filter заменяются генератором списков с условием. А reduce Гвидо хотел выпилить еще 15 лет назад, т.к. если в add_nums что-то сложнее a+b, то сходу трудно понять. Проще вынести в цикл.

Автогенератор таких статей:


import random

def write_part():
    item = random.choice(dir(__builtins__))
    print(f"# {item}")
    print(getattr(__builtins__,item).__doc__)

if __name__ == '__main__':
    print("Три объекта питона, о которых вы не знали (наверное)")
    write_part()
    write_part()
    write_part()

Алгоритимический метакопирайтинг.


И да, я не знал про __package__, memoryview и __import__. Забавно.

Вы забыли воспользоваться Google Translate API. Не самому же переводить.

Ну, как проект интересно, но уже не в комментах на хабре. Я и этот-то задолбался отлаживать.

Гугл-перевод — это конечно хорошо, но нужно его вычитывать перед публикацией. И желательно чтобы это делал человек, который хоть немного понимает, о чем речь в тексте и способен на чуть большее, чем согласование частей речи в предложении.


мы можем заранее объявить функцию, а затем передать ее в «filter()» вместе со списком итераций.

Что такое "список итераций"?


Тут конечно и в оригинале неверная формулировка


and then pass it to filter() alongside the list of iterables.

второй параметр у filter — это не "the list of iterables", а один iterable (частным случаем которого является list).
Но все же в оригинале фраза менее бредова, чем в переводе (прошу заметить, там the list of iterables, а не the list of iterations).


Обратите внимание, что «map()» сама вернет объект карты

Какой карты? Игральной? Географической?


Название функции map происходит от глагола to map, т.е. отображать. И адекватный перевод фразы map object в данном контексте — объект-отображение или что-то в этом духе. Или даже map-объект. Все лучше, чем какие-то карты, непонятно откуда взявшиеся.

Спасибо за замечание. Но как все-таки перевести про «the list of iterables»?

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

Как я уже писал, по хорошему эту фразу переводить не надо, так как она не верна. Вместо нее стоило бы написать "итерируемый объект (например список)" и добавить примечание что в оригинале ошибка. Если все же переводить дословно, то "список итерируемых объектов".

Список итерируемых объектов, например:


>>> list(map(lambda *x: x, (1,2), (3,4)))
[(1, 3), (2, 4)]
Что касается лично меня, то я практически всегда использую циклы. Все остальное только в случае крайней необходимости. Просто потому, что циклы на порядок легче читаются и понимаются.
Если же делаю что-то другое, тот же map() или list comprehension, то пишу коммент, где написано, что тут делается.

Зря. Некоторые вещи очень идиоматично получаются.


for token in map(str.strip, filter(line.split(','))):
     ....

Оно заменяет собой вот такое:


for raw_token in line.split(','):
    if not raw_token:
        continue
    token = raw_token.strip()

Лишний if с continue, лишние переменные без принципиального смысла. Вот если в map зафигачить лямбду с нетривиальным выражением — уже не очень.


Но это вопрос общей культуры.

Опять же я очень часто использую переменные без принципиального смысла. Чисто для того чтобы можно было:
— придя через неделю прочитать имя переменной и получить из этого имени подсказку, что происходит.
— Если что-то идет не так гораздо легче понять, где проблема в дебаггере или вставив print()
долго переходил с map-filter-reduce на list/dict comprehensions, циклы for — это даже читать не очень просто.
Сейчас циклы использую только если прям массово нужны side-effects в процессе обработки, или есть коллеги, которым тяжело читать coomprehensions.

И про эффективность не забываем, конечно
Использую list comprehension и как-то не было необходимости в вышеупомянутом
2020 год, если вдруг кто не заметил.
В качестве внекласного чтения рекомендую почитать pep на всякие comprehensions, и найти дату, когда reduce вынесли в модуль functools

p.s. аааа!!! pop-202, list comprehension создан в 2000 году…
Sign up to leave a comment.

Articles