В этой статье будут описаны малоизвестные, но полезные, функции Python. Многие функции из этого списка могут сильно уменьшить ваш код, оптимизировать его и сделать более читаемым.

Функция filter

Функция filter предназначена для "фильтрации" массива и может заменить цикл. Filter работает быстрее цикла, в некоторых случаях скорость работа программы увеличивается в десятки раз при использовании filter, вместо классических циклов.

Функция filter принимает на вход:

  1. Другую функцию, которая возвращает True или False

  2. Cписок, элементы которого будут подаваться на вход функции

В функции, передаваемой в filter, должно содержатся условие, которое определяет критерии для элементов нового массива. Если функция возвращает True - элемент добавляется в новый массив, если False - элемент не добавляется.

Функция filter возвращает объект класса Filter, используйте list(), чтобы переделать его в массив.

Filter поможет вам сделать код более оптимизированным и читаемым.

Например, вам дан массив a, надо все числа, которые меньше 10, записать в массив b и вывести его на экран.
Как это выглядит без использования filter:

a = [1, 10, 24, 6, 8, 19]
b = []
for i in range(len(a)):
	if a[i] < 10:
		b.append(a[i])
print(b)

Если использовать filter, то это выглядит так:

a = [1, 10, 24, 6, 8, 19]
b = list(filter(lambda x: x< 10, a))
print(b)

Код выглядит лаконичнее и работает быстрее.

Функция map

Функцией map, так же как и функцией filter, можно заменить циклы. Циклы работают медленнее чем map, но не каждый цикл можно заменить на map.

Функция map, принимает на вход:

  1. Функцию, которой передают каждый элемент массива

  2. Массив

Каждый элемент массива подается на вход функции. Конечный массив формируется из возвращаемых функцией значений.

Функция map позволяет сделать код красивее и ускорить его работу.

ля примера возьмем проблему, которая часто встречается у меня. Вам необходимо прочитать с клавиатуры 5 чисел, введённых через пробел и вывести их сумму на экран. Так как с клавиатуры читается строка, а не числа, необходимо их всех преобразовывать в числа.

Пример без использования map:

a = input().split(" ")
b = []
for i in range(5):
	b.append(int(a[i]))
print(b[0]+b[1]+b[2]+b[3]+b[4])

Программа с использованием map:

a = list(map(int, input().split(" ")))
print(a[0]+a[1]+a[2]+a[3]+a[4])

Программа с использованием map имеет меньший размер и работает быстрее, но ее можно сделать еще более быстрой с помощью следующей функции.

Функция reduce

Функция reduce работает так же как map, но reduce возвращает лишь одно значение которое получается из последнего выполнения переданной функции. Перед использованием reduce, его необходимо импортировать из модуля functools.

Функция reduce получает на вход:

  1. Функцию, которая получает более одно значения

  2. Массив, элементы которого будут поданы на вход функции

Reduce предает на вход функции элемент массива и выход предыдущего выполнения, при первом выполнении в функцию передаются первые элементы массива.

Для примера возьмем проблему которую рассматривали в прошлый раз.

Пример без reduce:

a = list(map(int, input().split(" ")))
print(a[0]+a[1]+a[2]+a[3]+a[4])

Пример с использованием reduce:

from functools import reduce
def summa(a, b):
	return a+b
print(reduce(summa, list(map(int, input().split(" ")))))

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

Множества

Множества это тип данных который работает быстрее остальных, но не может иметь повторяющихся элементов. Для создания множества используется функция set(), которой передается строка, массив, и так далее. Множества имеют методы и новые операторы:

  1. add() - добавление элемента в множество

  2. discard() - удаление элемента из множества

  3. union() - объединяет множество, из которого вызывается функция, с тем которое передается как аргумент

  4. intersection() - находит пересечение множеств, из которого вызывается, с тем которое передается как аргумент

  5. difference() - находит все элементы которые есть в множестве, из которого вызывают функцию, но которых нет в множестве переданном как аргумент

  6. symmetric_difference() - выдает множество, которое содержит все элементы из двух множеств, за исключением их общих элементов

  7. isdisjoint() - выдает True если оба множества не имею общих элементов, и False если имеют

  8. | - то же самое, что union

  9. & - то же самое, что intersection

  10. -(минус) - то же самое, что difference

  11. ^ - то же самое, что symmetric_difference

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

Функции any и all

Функции any и all используются вместо операторов OR и AND. Они позволяют сократить ваш код и сделать его более читаемым, в тех случаях когда вы используете большое количество условий в своей программе.

Они оба принимают массив из условий или булевых значений как аргумент, и возвращают одно булево значение.

Any - заменяет собой оператор OR, если в аргументах есть хотя бы одно True, на выходе будет True.

All - заменяет собой оператор AND, если в аргументах будет хотя бы один False, ответ будет False.

Для примера возьмем программу, которая должна вывести "1", если есть одинаковые переменные, и "-1", если таковых нет. Потом она должна проверить равна ли первая переменная второй и вторая третей, если это так вывести "2".

Пример без any и all:

a = input()
b = input()
c = input()
d = input()
if (a == b) OR (a == c) OR (a == d) OR (b == c) OR (b == d) OR (c == d):
	print("1")
else:
	print("-1")
if (a==b) AND (c == d):
	print("2")

Пример с использованием any и all:

a = input()
b = input()
c = input()
d = input()
if any([(a == b), (a == c), (a == d), (b == c), (b == d), (c == d)]):
	print("1")
else:
	print("-1")
if all([(a==b), (c == d)]):
	print("2")

Код стал приятнее глазу и немного уменьшился, эти функции уместно использовать, если вы используете большое количество условий для одного оператор if или while.

На этом все, я надеюсь вы узнали что-то новое. Если я что-то пропустил, то пишите в комментариях, если там будет много малоизвестных фишек Python, то я выпущу вторую часть.

Комментарии (22)


  1. js605451
    13.12.2021 02:56
    +38

    В этой статье будут описаны малоизвестные, но полезные, функции Python.

    Вы конечно хотели сказать "Я тут начал читать первую книжку по питону и во второй главе наткнулся на ..."


  1. SShtole
    13.12.2021 03:53
    +1

    for i in range(len(a))

    С Питоном не знаком, но нельзя как в Шарпе?

    for ai in a


    (Понятно, что filter ещё лучше, просто создание диапазона индексов при наличии перебирабельного объекта как-то странно смотрится).


    1. andreishal Автор
      13.12.2021 03:56
      -1

      Это лишь для примера, чтобы показать новичкам в Python как это работает.


    1. khmheh
      13.12.2021 04:05
      +3

      Можно. А если приспичит можно :

      for i, ai in enumerate(a):

      где i будет индекс, ai - указатель

      Ну тут надо ещё помнить, что при for it in collection итератор иммутабельный, поэтому for i in range для изменения коллекции тоже имеет место быть. Если правильно помню, в джаве похожая ситуация.


      1. masai
        13.12.2021 12:26

        Переменная, объявленная в цикле, — это самая обычная переменная, последовательно ссылающаяся на элементы списка. Если итерироваться по числам, то, так как сами числа неизменяемые, мы ничего не сломаем. А вот если элементы коллекции — это другие коллекции, то их можно изменять по ссылке.


        1. khmheh
          14.12.2021 15:57

          Ну, то есть, все изменяемые в контексте функций типы будут изменяемые и при итерировании


    1. DaneSoul
      13.12.2021 04:09

      (удалено, опередили комментарием выше)


    1. lair
      13.12.2021 12:28
      +6

      Не только можно, но и нужно.


      Вот только вместо filter истинный путь питона — это list comprehensions:


      a = [1, 10, 24, 6, 8, 19]
      b = [x for x in a if x < 10]
      print(b)


    1. enkryptor
      13.12.2021 21:24
      +2

      in range(len(a)) это популярный антипаттерн, вместо него рекомендуют писать просто in a или использовать функцию enumerate(), если нужен как сам элемент, так и его порядковый номер


  1. krote
    13.12.2021 05:14
    +8

    предложу автору еще один "малоизвестный" и очень лаконичный метод фильтрации:

    b = [c for c in a if c<10]

    ну и почитать учебник конечно же, а не только такие же статьи как вышенаписанная)


  1. goodwind
    13.12.2021 07:44

    Малоизвестные map и reduce. Впервые слышу.. Впервые слышу что они малоизвестны. Да и остальные сложно таковыми назвать


    1. dmitrysvd
      13.12.2021 08:29
      +2

      Более того, их обычно советуют неиспользовать. map заменяется генераторами, а reduce нужен в 1,5 случаях, для остального есть sum и math.prod


      1. 0xd34df00d
        13.12.2021 12:41
        +1

        Ради интереса погрепал свои ближайшие проекты — напрямую сумм и произведений нигде не нашлось, а вот разных сверток много.


  1. RC_Cat
    13.12.2021 10:49
    +2

    >Код выглядит лаконичнее и работает быстрее.

    В интернетах говорят что list comprehension быстрее.


  1. ScarferNV
    13.12.2021 12:12

    Map, filter, any, all... есть еще (first, *rest) и прочие плюшки функционального программирования, только кто этим пользуется в Питоне? Получается сборная солянка, пару строчек в одной парадигме, потом несколько строчек в другой...


    1. masai
      13.12.2021 12:28

      Простое использование map или any — это ещё не программирование в функциональной парадигме.


  1. cr0nk
    13.12.2021 13:47
    +1

    Хм.. Автор и вправду считает что это "Малоизвестные функции Python" ?


  1. OGR_kha
    13.12.2021 14:00

    Меня этим функциям учили на курсе для новичков.


  1. AlexKMK
    13.12.2021 14:03
    +1

    Map и reduce это не вы функции для упрощения работы с массивами. Это функции для реализации алгоритмов map/reduce.

    Но в целом можно и микроскопом гвозди забивать. Да.


  1. AlexKMK
    13.12.2021 14:04
    +1

    if (a == b) OR (a == c) OR (a == d) OR (b == c) OR (b == d) OR (c == d): print("1")

    А можно и :

    If a in [b,c,d]...


  1. spooky
    13.12.2021 19:11
    +2

    Совершенно не описана самая редкая функция +, а так же диаметрально противоположная ей -. Следующий цикл статей жду про еще менее распространенные * и /


  1. doltramir
    15.12.2021 02:23
    +1

    Уверен, есть статьи куда хуже этой, но мне такие не попадались. Это худшее из того, что я читал по Python.

    Зачем в перввом примере список?

    a = [1, 10, 24, 6, 8, 19]
    b = 0
    for i in a:
        if i < 10:
            b += i
    print(b)

    Ну а если прям совсем pythonic то вот:

    a = [1, 10, 24, 6, 8, 19]
    print(sum(i for i in a if i < 10))

    Второй пример такой же бред. И вот именно во втором примере от есть смысл:

    numbers = filter(None, input().split(' '))
    assert len(numbers) == 5
    print(sum(map(int, numbers)))

    В данном случае фильтр позволяет избавиться от лишних пробелов.

    Дальше ассертим, чтобы иметь точно 5 чисел (можно это убрать и будет иметь столько чисел, сколько ввели)

    Ну и мап чтобы всё сделать числами. Можно было бы ещё выкинуть всё, что не числа, но это уже не из условия и это заменит фильтр в начале.

    Дальше никому не нужный reduce (по многим причинам), но пусть будет так.

    Стоило бы добавить таую полезную и куда менее известную штуку, как модуль operator.

    from functools import reduce
    from operator import add
    print(reduce(add, list(map(int, input().split()))))

    Да, разделить строку можно и так, при чем это избавит от многих проблем.

    >>> '1  2 3, 6'.split()
    ['1', '2', '3,', '6']

    Множества оставим в покое, хотя тоже довольно известная штука.

    А вот дальше моя любимая часть. Потому, что код в ней не рабочий. Питон чувствителен к регистру, а значит

    abd, And, ANd, aNd, AND - это все разные вещи, и только первый вариант будет работать как логическое И.

    Ну и если уж так хочется сравнить все элементы коллекции между собой:

    from itertools import starmap, combinations
    from operator import eq
    
    inputs = a, b, c, d = [input() for _ in range(4)]
    any_equals = any(starmap(eq, combinations(inputs, 2)))
    print('1' if any_equals else '-1')
    if a == b and c == d:
      print('2')
      

    Теперь нам не нужно всё это делать вручную, так что мы точно не потеряем ни одну пару значений, сколько бы их не было. Ну и плюс можно чуть-чуть изменить код и мы получим программу, которая будет принимать любое количество элементов, а не только 4. Ну и разобьет всё, что мы ввели на пары, и выведет если значения в эти парах равны между собой (0 == 1, 2== 3, 4==5 ...) Тоже, что мы и так сделали, но для любого количества элементов.

    from itertools import starmap, combinations
    from operator import eq
    
    
    def check_inputs(input_count: int = 5):
      inputs = [input() for _ in range(input_count)]
        
    	any_equals = any(starmap(eq, combinations(inputs, 2)))
    	print('1' if any_equals else '-1')
    
      all_pairs_equal = all(starmap(eq, zip([iter(inputs)] * 2)))
      if all_pairs_equal:
        print('2')

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

    >>> values = list(range(10))
    >>> list(zip([iter(values)] * 2))
    [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
    >>> list(zip([iter(values)] * 3))
    [(0, 1, 2), (3, 4, 5), (6, 7, 8)]

    Это явно куда менее известные функции и варианты их использования. Кстати, в последнем случае можно использовать zip_longest.

    >>> from itertools import zip_longest
    >>> values = list(range(10))
    >>> list(zip_longest([iter(values)] * 2, fillvalue=-1))
    [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
    >>> list(zip_longest([iter(values)] * 3, fillvalue=-1))
    [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, -1, -1)]
    >>> list(zip_longest([iter(values)] * 3))
    [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]