Наталья Кайда 16 января 2023

🐍 Самоучитель по Python для начинающих. Часть 12: Анонимные функции

Изучаем особенности лямбда-функций в Python: разбираемся, когда стоит использовать анонимные функции вместо обычных, и в каких случаях lambda-выражения лучше заменить списковыми включениями. В конце статьи – 10 практических заданий с решениями.
🐍 Самоучитель по Python для начинающих. Часть 12: Анонимные функции

Происхождение названия

Лямбда-функции часто называют анонимными потому, что такую функцию можно определить с помощью оператора lambda, не давая ей название. Происхождение термина «лямбда» связано с формальной системой λ-исчисления.

Назначение и особенности анонимных функций в Python

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

        def discr(b, a, c):
    return b ** 2 - 4 * a * c

    

А так – анонимная:

        lambda b, a, c: b ** 2 - 4 * a * c
    

2. Анонимные функции используются однократно, в том участке кода, где были определены. Если функцию нужно вызывать многократно, следует написать обычную функцию def. Чаще всего анонимные функции используют совместно со встроенными функциями map(), filter(), sorted(), min(), max() и т. п. Особенно удобно применять лямбда-функции для сортировки:

        mydict = {'слива':5, 'папайя':6, 'лук':56, 'маракуйя':78, 'ежевика':45}

print('До сортировки: ', mydict)
sorted_mydict = dict(sorted(mydict.items(), key=lambda item: len(item[0])))
print('После сортировки: ', sorted_mydict)

    

Вывод:

        До сортировки:  {'слива': 5, 'папайя': 6, 'лук': 56, 'маракуйя': 78, 'ежевика': 45}
После сортировки:  {'лук': 56, 'слива': 5, 'папайя': 6, 'ежевика': 45, 'маракуйя': 78}

    

3. Присваивание лямбда-функциям имен не приводит к ошибке, но считается плохой практикой в соответствии с рекомендациями PEP8, поскольку противоречит самой концепции анонимности: такие функции используются однократно, и поэтому имена им не нужны:

        #Можно, но не нужно
max_num = lambda a, b : a if a > b else b

#Правильно
def max_num(a, b):
    return a if a > b else b 

    

4. Анонимные функции вызываются сразу после определения. Здесь лямбда-функция выполняет сортировку списка словарей по возрастанию цены:

        fruit = [
    {'название':'виноград',
     'цена': 230,
     'количество': 412
    },
    {'название':'манго',
     'цена': 350,
     'количество': 21
    },
    {'название':'бананы',
     'цена': 70,
     'количество': 234
    },
    {'название':'яблоки',
     'цена': 66,
     'количество': 213
    },
    ]
print(sorted(fruit, key=lambda item: item['цена']))

    

Вывод:

        [{'название': 'яблоки', 'цена': 66, 'количество': 213}, {'название': 'бананы', 'цена': 70, 'количество': 234}, {'название': 'виноград', 'цена': 230, 'количество': 412}, {'название': 'манго', 'цена': 350, 'количество': 21}]
    

5. Анонимные функции могут принимать неограниченное количество аргументов, но не могут содержать более одного выражения. Если в определении лямбда-функции записать два и более выражений, интерпретатор вернет ошибку:

        lambda a : a + 10, a - 5
    

Сообщение об ошибке:

        NameError: name 'a' is not defined
    

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

        def my_function(n):
  return lambda a : a * n
double = my_function(2)
print(double(15))

# Вывод:
30
    

7. В анонимных функциях можно использовать условия и операторы сравнения. Здесь анонимная функция добавляет слово гласная или согласная в список в зависимости от того, является ли буква в строке st гласной или согласной:

        st = 'яжертыуиопшщасдфгчйклзхцвбнм'
vowels = 'аиеёоуыэюя'
result = list(map(lambda x: 'гласная' if x in vowels else 'согласная', st))
print(result)

    

Вывод:

        ['гласная', 'согласная', 'гласная', 'согласная', 'согласная', 'гласная', 'гласная', 'гласная', 'гласная', 'согласная', 'согласная', 'согласная', 'гласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная', 'согласная']
    

А в этом примере анонимная функция выбирает из исходного списка четные числа, превосходящие 6:

        numbers = [7, 8, 6, 9, 4, -6, 2, 0, -3, -12, -5, -2, 12, 77, 32]
print(list(filter(lambda x: x > 6 and x % 2 == 0, numbers)))

    

8. Анонимные функции являются замыканиями – им доступны внешние переменные. В этом примере внутренняя лямбда-функция принимает список и возвращает минимальный или максимальный элемент в зависимости от соответствующего параметра внешней функции:

        def get_min_or_max(value='max'):
    return eval(f'lambda x: {value}(x)')
 
lst = [3, 5, -22, -15, 100, 7, 8, 9, 12, 98]
 
max_func = get_min_or_max()
min_func = get_min_or_max('min')
print(max_func(lst))
print(min_func(lst))

    

Вывод:

        100
-22

    

9. В отличие от обычных функций, анонимные не могут содержать return, pass, assert и raise.

10. Подобно обычным функциям, лямбда-выражения поддерживают различные типы аргументов:

  • позиционные;
  • именованные;
  • смешанные (позиционные + именованные);
  • *args;
  • **kwargs.

11. Лямбда-функции для проведения последовательных операций можно хранить в списках:

        ops = [(lambda x, y: x + y), (lambda x, y: x - y),
       (lambda x, y: x * y), (lambda x, y: x / y)]

x, y = int(input()), int(input())
for i in range(len(ops)):
    print(ops[i](x, y))

# Пример вывода для x = 12, y = 15:
27
-3
180
0.8
    

А также в словарях:

        ops = {'neg': lambda x: abs(x), 'pos': lambda x: x / 2, 'zero': lambda x: x + 5}
lst = list(map(int, input().split()))
for i in lst:
    if i < 0:
        print(ops['neg'](i))
    elif i > 0:
        print(ops['pos'](i))
    else:
        print(ops['zero'](i))

# Пример вывода для 4 -5 0 6 -3:
2.0
5
5
3.0
3

    

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

        my_list = [3, 5, 6, 7, 2, 8, 9, 10]
cubes = map(lambda x: pow(x, 3), my_list) # или x ** 3
print(list(cubes))

    

Однако тот же результат можно получить проще:

        print([i ** 3 for i in my_list])



    

Вывод:

        [27, 125, 216, 343, 8, 512, 729, 1000]
    

А здесь лямбда-функция используется для выбора четных чисел из списка:

        my_list = [4, 2, 3, 5, 16, 12, 2, 8, 9, 10]
print(list(filter(lambda x: x % 2 == 0, my_list)))

    

Вывод:

        [4, 2, 16, 12, 2, 8, 10]
    

Точно такой же результат даст простое списковое включение:

        print([i for i in my_list if i % 2 == 0])
    

Очень часто лямбда-функции используются совместно с filter():

        lst = ['ролл', 'яблоко', 'книга', 'шар', 'ноутбук']

print(list(filter(lambda x: len(x) > 4, lst)) )
print(list(filter(lambda x: 'р' in x, lst)))

    

В этом случае списковые включения также выглядят проще:

        print([i for i in lst if len(i) > 4])
print([i for i in lst if 'р' in i])

    

Вывод:

        ['яблоко', 'книга', 'ноутбук']
['ролл', 'шар']

    

Практика

Задание 1

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

  • с помощью reduce и лямбда-функции;
  • с math.prod();
  • с использованием пользовательской функции.

Пример ввода:

        3 5 6 7 8 2 4 3 4
    

Вывод:

        483840
    

Решение 1:

        from functools import reduce 
lst = list(map(int, input().split()))
print(reduce(lambda x, y: x * y, lst))

    

Решение 2:

        import math
lst = list(map(int, input().split()))
print(math.prod(lst))

    

Решение 3:

        def prod(lst):
    prod = 1
    for i in lst:
        prod *= i
    return prod

lst = list(map(int, input().split()))
print(prod(lst))

    

Задание 2

Напишите программу для сортировки значений словаря в алфавитном порядке.

Словарь:

        unsorted_dict = {'фрукт': 'яблоко', 'цвет': 'антрацит', 'артикул': 'в5678',
                 'модель': 'бабочка', 'наименование': 'книга', 'жанр': 'триллер'}

    

Ожидаемый результат:

        {'цвет': 'антрацит', 'модель': 'бабочка', 'артикул': 'в5678', 'наименование': 'книга', 'жанр': 'триллер', 'фрукт': 'яблоко'}
    

Решение:

        unsorted_dict = {'фрукт': 'яблоко', 'цвет': 'антрацит', 'артикул': 'в5678',
                 'модель': 'бабочка', 'наименование': 'книга', 'жанр': 'триллер'}
print(dict(sorted(unsorted_dict.items(), key=lambda item: item[1])))

    

Примечание: некоторые задачи, связанные с сортировкой словаря, можно также решать с помощью zip():

        print(dict(sorted(zip(unsorted_dict.values(), unsorted_dict.keys()))))
    

Задание 3

Напишите программу, которая получает от пользователя список, и выводит в алфавитном порядке все слова, состоящие из 5 букв. Решите задачу двумя способами – с использованием анонимной функции и с помощью спискового включения.

Пример ввода:

        физалис груша слива арбуз банан апельсин яблоко папайя
    

Вывод:

        арбуз банан груша слива
    

Решение 1:

        my_lst = input().split()
print(*sorted(list(filter(lambda x: len(x) == 5, my_lst))))

    

Решение 2:

        my_lst = input().split()
print(*sorted([i for i in my_lst if len(i) == 5]))

    

Задание 4

Напишите программу, которая:

  • определяет текущую дату с помощью datetime.datetime.now();
  • извлекает информацию о дне, месяце и годе с помощью анонимных функций;
  • выводит день, месяц и год на экран.

Пример вывода:

        день: 13
месяц: 1
год: 2023

    

Решение:

        import datetime
now = datetime.datetime.now()
date = [['день', lambda x: x.day], ['месяц', lambda x: x.month], ['год', lambda x: x.year]]
for i in date:
    print(f'{i[0]}: {i[1](now)}')

    

Примечание: без лямбда-функций задачу можно решить так:

        import datetime
now = str(datetime.datetime.now())
date = now.split()[0].split('-')
print(f'день: {date[2]}'
      f'\nмесяц: {date[1]}'
      f'\nгод:  {date[0]}'
      )

    

Задание 5

Напишите программу, которая получает целое число n и выводит n первых элементов последовательности Фибоначчи.

Пример ввода:

        12
    

Вывод:

        0 1 1 2 3 5 8 13 21 34 55 89
    

Решение:

        from functools import reduce
 
fibonacci = lambda n: reduce(lambda x, n: x + [x[-1] + x[-2]], range(n - 2), [0, 1])
n = int(input())
print(*fibonacci(n))

    

Задание 6

Напишите программу, которая получает от пользователя две строки с целыми числами и находит пересечение строк (общие для обеих строк элементы) без использования множеств.

Пример ввода:

        3 6 7 8 9 2 1 0 12 45
7 9 0 12 15 5 6 11 43

    

Вывод:

        Пересечение: 7 9 0 12 6
    

Решение:

        lst1 = list(map(int, input().split()))
lst2 = list(map(int, input().split()))

intersection = list(filter(lambda x: x in lst1, lst2)) 
print('Пересечение:', *intersection)

    

Задание 7

Напишите программу, которая сортирует полученную от пользователя строку с числами следующим образом:

  • отрицательные числа идут после положительных;
  • и положительные, и отрицательные числа упорядочены по возрастанию.

Пример ввода:

        -6 -1 3 -5 -15 4 1 9 8 6 -3 -4 12 -10 7
    

Вывод:

        1 3 4 6 7 8 9 12 -15 -10 -6 -5 -4 -3 -1
    

Решение:

        lst = list(map(int, input().split()))
print(*sorted(lst, key = lambda i: 0 if i == 0 else -1 / i))

    

Задание 8

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

Пример ввода:

        7 2 3 -5 6 7 0 1 12 45 9 33
32 87 12 -2 4 7 1 3 9 2 4 2

    

Вывод:

        39 89 15 -7 10 14 1 4 21 47 13 35
    

Решение:

        lst1 = list(map(int, input().split()))
lst2 = list(map(int, input().split()))

print(*list(map(lambda x, y: x + y, lst1, lst2)))

    

Примечание: другой способ решения задачи – с использованием zip():

        for i, j in zip(lst1, lst2):
    print(i + j, end=' ')

    

Задание 9

Напишите программу, которая получает 1) строку с набором слов; 2) одно слово, не входящее в предыдущую строку, и находит все анаграммы слова в строке.

Пример ввода:

        каприз клоун колба колун крона уклон колыбель карта
кулон

    

Вывод:

        клоун колун уклон
    

Решение:

        lst = input().split()
word = input()
print(*list(filter(lambda x: (set(word) == set(x)), lst)))

    

Задание 10

Напишите программу для проверки надежности пароля. Надежный пароль должен содержать не менее 9 символов; кроме того, в пароле должна быть хотя бы одна:

  • буква в верхнем регистре;
  • буква в нижнем регистре;
  • цифра;
  • и хотя бы один символа из набора !@#$%^&*()-+.

Пример ввода 1:

        Введите пароль для проверки:  password

    

Вывод:

        В пароле должна быть хотя бы одна буква в верхнем регистре 
В пароле должна быть хотя бы одна цифра 
Пароль должен содержать один из спецсимволов !@#$%^&*()-+ 
Пароль должен содержать не менее 9 символов

    

Пример ввода 2:

        Введите пароль для проверки:  Mur#zilka98
    

Вывод:

        Надежный пароль
    

Решение:

        def check_password(passw):
    res_dict = [
    lambda passw: any(x.isupper() for x in passw) or '\nВ пароле должна быть хотя бы одна буква в верхнем регистре',
    lambda passw: any(x.islower() for x in passw) or '\nВ пароле должна быть хотя бы одна буква в нижнем регистре ',
    lambda passw: any(x.isdigit() for x in passw) or '\nВ пароле должна быть хотя бы одна цифра',
    lambda passw: any(x in '!@#$%^&*()-+' for x in passw) or '\nПароль должен содержать один из спецсимволов !@#$%^&*()-+',
    lambda passw: len(passw) >= 9 or '\nПароль должен содержать не менее 9 символов']
    result = [x for x in [i(passw) for i in res_dict] if x != True]
    if not result:
        result.append('Надежный пароль')
    return result    
st = input('Введите пароль для проверки: ')
print(*check_password(st))
    

Подведем итоги

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

В следующей статье будем изучать рекурсивные функции.

***

Содержание самоучителя

  1. Особенности, сферы применения, установка, онлайн IDE
  2. Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
  3. Типы данных: преобразование и базовые операции
  4. Методы работы со строками
  5. Методы работы со списками и списковыми включениями
  6. Методы работы со словарями и генераторами словарей
  7. Методы работы с кортежами
  8. Методы работы со множествами
  9. Особенности цикла for
  10. Условный цикл while
  11. Функции с позиционными и именованными аргументами
  12. Анонимные функции
  13. Рекурсивные функции
  14. Функции высшего порядка, замыкания и декораторы
  15. Методы работы с файлами и файловой системой
  16. Регулярные выражения
  17. Основы скрапинга и парсинга
  18. Основы ООП: инкапсуляция и наследование
  19. Основы ООП – абстракция и полиморфизм
  20. Графический интерфейс на Tkinter
  21. Основы разработки игр на Pygame
  22. Основы работы с SQLite
  23. Основы веб-разработки на Flask
  24. Основы работы с NumPy
  25. Основы анализа данных с Pandas
***
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
DevOps
Санкт-Петербург, от 150000 RUB до 400000 RUB
Продуктовый аналитик в поддержку
по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ