Позиционные аргументы можно получать в виде *args
, причём произвольное их количество. Для именованных аргументов тоже существует подобная возможность. Только именованные аргументы получаются в виде словаря, что позволяет сохранить имена аргументов в ключах:
def g(**kwargs):
return kwargs
g(x=1, y=2, z=None)
# {'x': 1, 'y': 2, 'z': None}
По соглашению аргумент, получающий подобный словарь, принято называть
kwargs
от словосочетания "keyword arguments".
Аргумент *args
в определении функции пишется после всех обычных позиционных аргументов перед первым аргументом со значением по умолчанию, а **kwargs
пишется в самом конце, после последнего аргумента со значением по умолчанию. Давайте определим функцию, которая принимает все виды аргументов:
def f(x, y, *args, kx=None, ky=42, **kwargs):
return (x, y, args, kx, ky, kwargs)
f(1, 2, 3, 4, kx='a', ky='b', kz='c')
# (1, 2, (3, 4), 'a', 'b', {'kz': 'c'})
Не нужно пугаться, в реальном коде редко какая функция использует все эти возможности одновременно! Но понимать, как каждая отдельная форма объявления аргументов работает и как такие формы можно сочетать — очень важно!
Как и в случае позиционных аргументов, именованные можно передавать в функцию "пачкой" в виде словаря. Для этого нужно перед словарём поставить… две звёздочки! Пример:
def coords(x, y):
return (x, y)
coords(x=1, **{'y': 2})
# (1, 2)
Как вы видите, я и обычный именованный аргумент указал, и завернул аргументы в словарь — так тоже можно и это даже удобно! Попробуем функцию f
из примера в начале урока вызвать с двумя наборами аргументов — одним для позиционных и вторым для именованных:
positional = (2, 3)
named = dict(ky='b', kz='c')
f(1, *positional, 4, kx='a', **named)
# (1, 2, (3, 4), 'a', 'b', {'kz': 'c'})
Обратите внимание на то, как я сконструировал словарь: я не написал литерал, а вместо этого вызвал функцию dict
с несколькими именованными аргументами — так словарь ещё больше похож на "сохранённый набор аргументов"!
Также отметьте, что при подстановке аргументов "разворачивающиеся" наборы аргументов вроде *positional
и **named
можно указывать вперемешку с аргументами соответствующего типа: *positional
с позиционными, а **named
— с именованными. И конечно же, все именованные аргументы должны идти после всех позиционных!
В Python 3 добавили возможность пометить именованные аргументы функции так, чтобы вызывать функцию можно было, только передав эти аргументы по именам. Такие аргументы называются keyword-only и их нельзя передать в функцию в виде позиционных. Выглядит функция с подобными аргументами так:
def open_file(name, *, writable=False, binary=False):
…
f1 = open_file('foo.txt', writable=True)
f2 = open_file('bar.bin', binary=True)
f3 = open_file('raw.dat', True, True)
# TypeError: open_file() takes 1 positional argument but 3 were given
Здесь *
выступает разделителем: отделяет "обычные" аргументы (их можно указывать и по имени, и позиционно) от "строго именованных". Такой разделитель можно использовать только один раз в одном определении. А ещё его нельзя применять в функциях с *args
— да, не очень логично, но так уж вышло. Зато можно объявлять функции, у которых будут только строго именованные аргументы, для этого нужно поставить звёздочку в самом начале перечня аргументов.
Этот пример неплохо демонстрирует подход к описанию аргументов. Первый аргумент — имя файла, который будет открыт. Имя файла всегда присутствует, ведь нужно же что-то открыть, и связано по смыслу с именем функции. Поэтому этот аргумент можно не именовать. А вот writable
и binary
— необязательные аргументы. Которые получают ещё и ничего не говорящие значения True
/False
. Вполне логично ожидать, что вызов вида open_file('raw.dat', True, True)
мало кому понравится! Поэтому опции и объявлены так, что могут быть указаны только явно.
Отдельно стоит отметить, что при вызове функций вы имеете чуть больше свободы для задания порядка аргументов. Одиночные именованные аргументы могут идти вперемешку с подстановками наборов позиционных. Вот пример такого вызова:
foo = [1, 2, 3]
bar = "abc"
# функция f здеcь та же, что и в первом разделе урока
f(kx=42, *foo, ky=100, *bar)
# (1, 2, (3, 'a', 'b', 'c'), 42, 100, {})
Ещё одна особенность заключается в том, что вы не можете для функции с сигнатурой вида f(x, *args)
указать одновременно аргумент x
по имени и при этом развернуть набор параметров. То есть вы не сможете сделать так: f(*foo, x=42)
.
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт