4 Продвинутых приема работы с функциями Python, о которых вы могли не знать

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

1. Принудительно задаваемые именованные аргументы

У именованных аргументов есть несколько преимуществ:

  • Здесь нет какой-то обязательной очередности задания аргументов. Имеет значение имя, а не позиция.
  • Именованные аргументы придают больше ясности. Не глядя на саму функцию, часто бывает легко по названию аргумента догадаться, для чего он используется.

Возможно, все это вам уже знакомо. Но знаете ли вы о возможности принудительно задавать именованные аргументы? Подробно это описано в PEP 3202. Если вкратце, то все сводится к использованию звездочки перед каждым аргументом, который будет принудительно задан в качестве именованного аргумента. Или сразу перед всеми, и тогда все аргументы будут заданы как именованные аргументы:

>>> def f(*, a, b):
...     print(a, b)
...
>>> f(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional 
           arguments but 2 were given
>>> f(a=1, b=2)
1 2
>>>

2. Использование * и ** для распаковывания аргументов функции

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

Распаковывается словарь для использования с именованными аргументами с помощью префикса **:

>>> def f(a, b):
...     print(a, b)
...
>>> args = { "a": 1, "b": 2 }
>>> f(**args)
1 2

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

>>> def f(a, b, c):
...    print(a, b, c)
...
>>> l = [1, 2, 3]
>>> f(*l)
1 2 3

3. Декорирование функций

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

Создадим наш собственный декоратор. Это проще, чем кажется, и когда-нибудь он пригодится:

def print_argument(func):
    def wrapper(the_number):
        print("Argument for", func.__name__, "is", the_number)
        return func(the_number)
    
    return wrapper

@print_argument
def add_one(x):
    return x + 1

print(add_one(1))

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

С помощью @print_argument мы применяем декоратор к функции. На всякий случай заметим, что этот декоратор используется и для других функций тоже.

В качестве результата нашего маленького скрипта будет выводиться:

Argument for add_one is 1
2

4. Анонимные функции

Иногда функциям не присваивают имен. Например, когда уверены, что функция будет использована только раз. Для таких случаев в Python предусмотрены анонимные функции (также называемые лямбда-функциями).

Лямбда-функция назначается переменной, и таким образом получается лаконичный способ определения функции:

>>> def add_one(x): return 2*x
>>> add_one(3)
4

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

>>> numbers = [1, 2, 3, 4]
>>> times_two = map(lambda x: x * 2, numbers)
>>> list(times_two)
[2, 4, 6, 8]
>>>

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

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Erik van Baaren: 4 Advanced Tricks With Python Functions You Might Not Know

Предыдущая статьяСтилизация фотографий под мультфильмы с помощью Python
Следующая статьяПочему большинство инженеров ПО не пишут документацию?