Функции с произвольным числом параметров *args и **kwargs

Курс по Python: https://stepik.org/course/100707

На этом занятии узнаем, как функциям можно передавать произвольное число аргументов. Где это используется, я думаю, вы понимаете? Например, известная нам функция

max(1, 2, 3, -4)

может принимать разное число аргументов и возвращает максимальное значение. Как можно самим определять такие функции? Делается это очень просто. Допустим, мы с вами хотим задать функцию для формирования маршрута к файлу:

F:\~stepik.org\Добрый, добрый Python (Питон)\39\p39. Функции.docx

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

def os_path(*args):
    print(args)

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

os_path("F:\\~stepik.org", "Добрый, добрый Python (Питон)", "39\\p39. Функции.docx")

Не забываем здесь про экранирование обратных слешей. Выполним эту программу и в консоли видим, что переменная args ссылается на кортеж со значениями переданных трех аргументов. Здорово, да?! Чтобы функция принимала произвольное число аргументов, в ее объявлении достаточно у параметра прописать оператор *. Это оператор упаковки аргументов в кортеж и через переменную args мы сможем с ним работать.

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

def os_path(*args):
    path = "\\".join(args)
    return path

И, далее, вызвать эту функцию:

p = os_path("F:\\~stepik.org", "Добрый, добрый Python (Питон)", "39\\p39. Функции.docx")
print(p)

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

p = os_path("F:\\~stepik.org",
            "Добрый, добрый Python (Питон)",
            "39\\p39. Функции.docx",
            sep='/'
)

При запуске программы увидим ошибку, что функция не имеет такого формального параметра. Дело в том, что записывая объявление *args мы определяем лишь произвольное число фактических параметров, но не формальных. Как это можно поправить? Здесь есть, по крайней мере, два способа. В самом простом варианте, достаточно прописать этот формальный параметр в объявлении функции:

def os_path(*args, sep='\\'):
    path = sep.join(args)
    return path

И теперь никаких проблем с вызовом нет. Но, конечно, указать, какой-либо другой именованный аргумент мы не можем:

p = os_path("F:\\~stepik.org",
            "Добрый, добрый Python (Питон)",
            "39\\p39. Функции.docx",
            sep='/', trim=True
)

Снова получим ту же самую ошибку. Так как же определить в функции произвольное число формальных параметров? Делается это с помощью следующего синтаксиса:

def os_path(*args, **kwargs):
    print(kwargs)
    path = kwargs['sep'].join(args)
    return path

Мы прописываем уже две звездочки, а затем, имя переменной, которая будет ссылаться на упакованные значения в виде словаря. Убедимся в этом, выполним программу и смотрите, в консоли коллекция kwargs действительно представляет собой словарь, ключами которого являются имена аргументов, а значениями – значения аргументов. Все очень удобно и просто, как всегда в Python!

Причем, коллекция **kwargs обязательно должна быть записана после коллекции *args, наоборот нельзя, так как вначале должны идти фактические параметры и только потом – формальные. Мало того, мы можем некоторые параметры указывать явно, например:

def os_path(*args, sep='\\', **kwargs):
    path = sep.join(args)
    return path

И, тем самым, гарантировать их существование внутри функции. А другие, передаваемые именованные аргументы, следует проверять, прежде чем использовать, например, для параметра trim сначала делаем проверку его существования в словаре kwargs, а затем, смотрим, чему равно это значение:

def os_path(*args, sep='\\', **kwargs):
    if 'trim' in kwargs and kwargs['trim']:
        args = [x.strip() for x in args]
 
    path = sep.join(args)
    return path

Если условие выполняется, то удаляем пробелы до и после фрагментов путей к файлу.

То же самое и с фактическими параметрами. Некоторые из них можно явно указать, при объявлении функции:

def os_path(disk, *args, sep='\\', **kwargs):
    args = (disk,) + args
 
    if 'trim' in kwargs and kwargs['trim']:
        args = [x.strip() for x in args]
 
    path = sep.join(args)
    return path

И тогда на первый аргумент будет ссылаться параметр disk, а остальные позиционные аргументы упаковываться в коллекцию args:

p = os_path("F:", "~stepik.org",
            "Добрый, добрый Python (Питон)",
            "39\\p39. Функции.docx",
            sep='/', trim=True
)

Вот принцип, по которому объявляются функции с произвольным числом фактических и формальных параметров.

Вам осталось закрепить этот материал практическими заданиями и жду всех вас на следующем уроке.

Курс по Python: https://stepik.org/course/100707

Видео по теме