*args в Python: сколько аргументов угодно
Функция, которой нельзя заранее сказать, сколько значений придет: два числа, пять или ни одного. Разбираем звёздочку: упаковку в кортеж и распаковку при вызове.
Редакция Питоники
До сих пор число аргументов у наших функций было вшито в объявление: функция про аргументы требовала ровно столько значений, сколько параметров объявлено. Но жизнь вносит поправки: суммировать нужно два числа, потом пять, потом вообще ни одного. Писать под каждый случай отдельную функцию — та же копипаста, от которой мы когда-то ушли. Нужна функция, которая примет сколько угодно значений. Для этого у Python есть звёздочка.
Что такое *args в Python: коротко
Звёздочка в объявлении функции собирает все позиционные аргументы вызова в один кортеж: def total(*nums). Функция становится безразличной к числу аргументов — их может быть два, пять или ни одного. Внутри args — обычный кортеж, который перебирают циклом или отдают sum. Знак звёздочки при вызове делает обратное: f(*data) распаковывает список в отдельные аргументы.
Проблема: сколько значений придёт
def sum2(a, b):
return a + b
*args: упаковка в кортеж
Звёздочка перед именем параметра меняет правила: теперь функция примет любое число позиционных аргументов и соберёт их в кортеж.
def show(*args):
print(args)
print(type(args))
show(10, 20, 30)
(10, 20, 30) <class 'tuple'>
Три значения, переданные через запятую, превратились в один кортеж — набор из трёх чисел в круглых скобках. Звёздочка в объявлении функции собирает все позиционные аргументы вызова в один кортеж. Имя args — соглашение, а не требование языка: *nums или *values работают так же, хотя имя args встречается в коде чаще всего. Главное свойство кортежа — с ним внутри функции можно делать всё то же, что и с обычными данными: перебирать циклом, считать сумму, проверять длину.
Сумма любого числа аргументов
Классика применения — суммирование. Аккумулятор из курса циклов перебирает кортеж, и функция становится безразличной к числу аргументов:
def total(*nums):
acc = 0
for n in nums:
acc += n
return acc
print(total(1, 2, 3))
print(total(10, 20, 30, 40))
print(total())
6 100 0
Три вызова — три разных числа аргументов, и все сработали. Даже пустой вызов легален: кортеж окажется пустым, цикл не выполнится ни разу, и вернётся стартовое значение аккумулятора — ноль. Цикл-аккумулятор можно заменить встроенной функцией sum(nums) из урока про встроенные функции — тела функции станет одна строка. А вот пример с другим правилом: максимум из любого числа значений — по каркасу максимума из курса циклов:
def biggest(*nums):
return max(nums)
print(biggest(3, 9, 4))
print(biggest(17))
9 17
Обычные аргументы и *args вместе
Звёздочка не отменяет обычные параметры — они просто идут первыми. Всё, что встанет в вызове до положенного числа обычных аргументов, попадёт в них, а всё остальное — в кортеж:
def report(title, *values):
print('Отчёт:', title)
for v in values:
print(' -', v)
report('Итоги дня', 5, 3, 8)
Отчёт: Итоги дня - 5 - 3 - 8
Первый аргумент вызова встал в параметр title, остальные три упаковались в кортеж values. Отсюда жёсткое правило: после параметра со звёздочкой обычных позиционных параметров быть не может — Python уже не сможет понять, где кончается кортеж и начинается следующий обычный аргумент. Параметры после звёздочки можно передавать только по имени:
def report(*lines, footer):
for line in lines:
print(line)
print(footer)
report('Начало', 'Конец', footer='готово')
report('Начало', 'Конец', 'готово')
Распаковка: звёздочка при вызове
Звёздочка умеет и обратную операцию. В объявлении она собирает аргументы, а при вызове — раскладывает список на отдельные аргументы. Разница видна на одном примере:
def show(*args):
print(args)
data = [10, 20, 30]
show(data)
show(*data)
([10, 20, 30],) (10, 20, 30)
Вызов show(data) передал один аргумент — список целиком, и кортеж получился из одного элемента, внутри которого лежит список. Вызов show(*data) распаковал список: три отдельных числа вошли в кортеж на равных. Упаковка в объявлении и распаковка при вызове — зеркальные операции одной звёздочки. Распаковывать можно и кортежи, и любые перебираемые наборы. Соберём итоговый пример: приветствие любому числу гостей — заголовок обязателен, имена — сколько пришлют:
def greet_all(greeting, *names):
for name in names:
print(f'{greeting}, {name}!')
greet_all('Привет', 'Анна', 'Борис')
greet_all('Пока', 'Вера')
Привет, Анна! Привет, Борис! Пока, Вера!
| Обычный параметр | Параметр со звёздочкой |
|---|---|
| Принимает ровно один аргумент | Принимает любое число аргументов — от нуля |
| Аргумент приходит как есть | Аргументы собираются в кортеж |
| Обязателен, если нет умолчания | Вызов без него легален |
| До звёздочки — позиционные и именованные | После звёздочки — только по имени |
Позиционные значения научились собирать в кучу. Но бывают и именованные аргументы, которых заранее не знаешь: цвет, размер, режим — какие именуемые настройки пришлют в следующий раз? Для них у звёздочки есть сестра с двумя знаками — о ней следующий урок.
args — это обычный кортеж
Первое, что стоит понять: внутри функции args — не какая-то особая сущность, а обычный кортеж. С ним можно делать всё, что с кортежом: мерить длину, брать элемент по индексу, резать, распаковывать. Превращать его в список заранее не нужно — это лишняя работа.
def show(*args):
print(len(args))
print(args[0])
print(args[-1])
print(args[:2])
show(10, 20, 30, 40)
4 10 40 (10, 20)
Четыре аргумента — длина четыре, первый элемент десять, последний сорок, срез из двух — пара (10, 20). Так что если внутри функции нужен список, который можно менять, — это отдельная история: кортеж неизменяем, и про это отдельный pitfall ниже.
Звёздочка в присваивании
У звёздочки есть ещё одно применение, о котором забывают: в левой части присваивания она забирает «всё остальное» в список. Механизм тот же, только направление другое — в *args она собирает, здесь раскидывает.
first, *rest = [10, 20, 30, 40]
print(first)
print(rest)
head, *middle, tail = [1, 2, 3, 4, 5]
print(head, middle, tail)
10 [20, 30, 40] 1 [2, 3, 4] 5
В первой строке first забрал первый элемент, а rest — все остальные, и собрались они в список, а не в кортеж. Во второй строке звёздочка стоит посередине: всё, что между head и tail, уходит в middle. С таким приёмом удобно работать с результатом, где важен первый элемент, а с остальными — просто вместе: например, отделить заголовок файла от данных.
Когда *args лишний
Звёздочка — не универсальный ответ на вопрос «а как передать список». Если данные всегда приходят одним списком, честнее объявить параметр-список: вызов останется коротким, а в отладке будет видно, где данные приходят.
| Ситуация | Как объявить | Почему так лучше |
|---|---|---|
| Данные всегда приходят списком | Один параметр-список | Вызов не размазывается по строкам |
| Значения однотипные и их много | Параметр-список и цикл | Проще читать и проверять |
| Значения передаются поштучно, число неизвестно | *args | Вызов остаётся коротким: total(1, 2, 3) |
| Набор нужно передать дальше | *args и f(*args) | Данные не теряются при пересылке |
Мы держимся правила: набор — значит параметр-список, а *args оставляем для вызовов, где значения идут поштучно. Смешивать оба подхода в одной функции не стоит: через месяц никто не вспомнит, почему в одном месте значения передаются через запятую, а в другом — одним списком.
Практика: отчёт с произвольным числом строк
Соберём вместе всё, что разобрали: заголовок обязателен, строки — сколько пришлют, а в конце подпись передаётся только по имени.
def report(title, *lines, footer='конец отчёта'):
print('===', title, '===')
for line in lines:
print(' -', line)
print('---', footer)
report('Продажи за день', 'хлеб — 45', 'молоко — 80')
report('Склад', 'хлеб — 12', 'молоко — 3', footer='данные на 18:00')
=== Продажи за день === - хлеб — 45 - молоко — 80 --- конец отчёта === Склад === - хлеб — 12 - молоко — 3 --- данные на 18:00
Два вызова — разное число строк и разная подпись, а тело функции одно. Именно так выглядит код, который перестаёт размножаться: вместо report2, report3 и report7 у вас одна функция, которая принимает столько данных, сколько нужно.
Всё вместе: обычные, *args и именованные
Звёздочки можно комбинировать, и самая полная форма сигнатуры выглядит так: def report(title, *values, footer='конец', **options). Слева направо: обязательный позиционный параметр, потом произвольное число позиционных, потом параметры только для именованной передачи, потом произвольное число именованных.
def report(title, *values, footer='конец', **options):
print('===', title, '===')
print(' значения:', values)
print(' подпись:', footer)
print(' опции:', options)
report('Продажи', 5, 3, footer='итог', unit='руб', city='Казань')
=== Продажи ===
значения: (5, 3)
подпись: итог
опции: {'unit': 'руб', 'city': 'Казань'}Позиционные значения уехали в кортеж values, параметр после звёздочки — в footer, а всё, что не нашло своего места, — в словарь options. Правило разбора простое: сначала обычные позиционные, потом *args забирает остаток позиционных, потом параметры после звёздочки принимают только имена, а **kwargs забирает все оставшиеся именованные.
| Форма в сигнатуре | Что собирает | Чем достать |
|---|---|---|
| обычный параметр | ровно одно значение | само значение |
| *args | все остальные позиционные | кортеж args |
| параметр после *args | только именованную передачу | по имени: f(x, footer='готово') |
| **kwargs | все остальные именованные | словарь kwargs |
Сначала предскажи ответ в голове — это главный навык программиста.
def total(*nums):
return sum(nums)
print(total(1, 2))
print(total(5, 5, 5, 5))
def demo(a, *rest):
print(a, rest)
demo(1, 2, 3)
def show(*args):
print(args)
values = [4, 9]
show(values)
show(*values)
1. Что такое args внутри функции def f(*args)?
2. Функция total(*nums) считает сумму аккумулятором. Что вернёт вызов total() без аргументов?
3. Сколько обычных позиционных параметров можно объявить после *args?
4. Что делает звёздочка в вызове f(*prices), где prices — список?
5. Что произойдёт при вызове args.append(10) внутри функции со звёздочкой?
6. Чем отличается вызов show(data) от show(*data) у функции def show(*args)?
7. Что напечатает print(args[0]) внутри функции f(*args), вызванной как f(1, 2, 3)?
8. Что получится после выполнения first, *rest = [1, 2, 3]?
9. Когда вместо *args лучше объявить обычный параметр-список?
Напиши функцию average(*numbers), которая возвращает среднее арифметическое любого числа аргументов (сумма, делённая на количество). Вызови её с тремя числами 2, 4, 6 и с пятью числами 1, 2, 3, 4, 5 — оба результата напечатай.
Как принять в функцию переменное число аргументов в Python?
Объявить параметр со звёздочкой: def total(*nums). Все позиционные аргументы вызова соберутся в кортеж nums, который перебирают циклом или отдают встроенной функции sum(nums). Вызов без аргументов тоже легален — кортеж будет пустым.
Урок 12: встроенные функции PythonЧем звёздочка в объявлении отличается от звёздочки при вызове?
В объявлении def f(*args) звёздочка упаковывает: все аргументы вызова собираются в один кортеж. При вызове f(*data) звёздочка распаковывает: элементы списка становятся отдельными аргументами. Одно знание работает в обе стороны — это зеркальные операции.
Можно ли передать список в функцию с *args?
Да, двумя способами. f(список) передаст его как один цельный аргумент — он ляжет в кортеж единственным элементом. f(*список) распакует список, и каждый элемент станет отдельным аргументом. Второе обычно и нужно: например, f(*prices) для функции, складывающей цены.
Что за параметры, которые после *args можно передать только по имени?
Такие параметры называют keyword-only: они объявлены после звёздочки, и позиционно их не передать — только именем, как f(1, 2, footer='готово'). Иначе Python отвечает TypeError о пропущенном keyword-only аргументе. Обычные именованные аргументы и умолчания разобраны в отдельном уроке.
Урок 4: умолчания и именованные аргументыКак узнать, сколько аргументов пришло в функцию с *args?
По длине кортежа: len(args). args — обычный кортеж, поэтому работают и индексация args[0], и срез args[:2], и распаковка в несколько переменных.
Чем *args отличается от обычного параметра-списка?
Способом вызова. С параметром-списком данные приходят одним набором: f(prices). С *args значения передаются поштучно: total(1, 2, 3), и функция не знает заранее, сколько их будет. Если данные всегда приходят набором — выбирайте параметр-список.
Что означает first, *rest = [1, 2, 3]?
Распаковку с остатком: first заберёт первый элемент, а rest соберёт все остальные в список. Звёздочка в присваивании работает как зеркало *args — раскладывает набор, а не собирает его.
Понравился урок? Сошлитесь на него
«Звёздочка в объявлении функции собирает все позиционные аргументы вызова в один кортеж.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Функции · Урок 2
Аргументы функции в Python: данные внутрь
Функция принимает данные через параметры: greet('Анна') здоровается с Анной. Разбираем позиционные аргументы, порядок и количество — и типичную ошибку TypeError.
Функции · Урок 4
Умолчания и именованные аргументы в Python
Необязательные параметры с запасными значениями и вызов по имени: def f(x, n=2) и f(x=5, n=3). Разбираем порядок параметров и знакомимся с ловушкой изменяемого умолчания.
Функции · Урок 11
**kwargs в Python: именованные на подхвате
Позиционные аргументы умеет собирать звёздочка — а какие именованные прислал вызов, заранее не знал никто. Разбираем двойную звёздочку и словарь настроек.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Множества · Урок 14
Функция возвращает несколько значений: return с кортежем
Функция возвращает пару значений одной строкой: запятая в return собирает кортеж, распаковка раскладывает его по переменным. min и max за один проход, звёздочка для остатка.
функция возвращает кортеж pythonpython несколько значений из функции
pytest · Урок 18
Что тестировать: стратегия, границы и покрытие
Механику pytest ты знаешь — осталось ответить на главный вопрос: какие тесты писать. Поведение вместо реализации, границы диапазонов, ветки ошибок, покрытие и пирамида.
тестирование веток функциисколько тестов нужно писать
Функции · Урок 1
Функции в Python: первая def
Первая функция: задаём блоку кода имя через def и вызываем по скобкам. Разбираем объявление на части — имя, двоеточие, тело с отступом — и видим, зачем функция вместо копипасты.
функции в pythonpython функции