Что значит 2 звездочки в питоне
Перейти к содержимому

Что значит 2 звездочки в питоне

  • автор:

Что значит * (звёздочка) и ** двойная звёздочка в параметрах функций?

Звёздочка в Питоне помимо умножения x*y ( help(‘*’) ) и возведения в степень x**y ( help(‘**’) ) † используется, чтобы обозначить ноль или более чего-либо.

К примеру в описании параметров функции:

def f(*args): print(args) 
>>> f(1,'a') (1, 'a') 

Для именованных параметров используются две звёздочки:

def g(a, b, *args, name='default', **kwargs): print(a, b, args, name, kwargs) 

здесь g() принимает два обязательных аргумента и произвольное (ноль или более) количество других аргументов:

>>> g(1, b=2, c=3) 1 2 () default

kwargs —это словарь дополнительных аргументов, переданных по имени ( c в данном случае). А args это пустой кортеж () , так как дополнительных позиционных аргументов не было передано.

После * все параметры обязаны передаваться по имени, пример:

def min_item(items, *, key=lambda x: x): . 

При вызове, если задан, key обязан быть указан по имени: min([1,2,-3], key=abs) .

Принятие произвольного количества аргументов может быть полезно при создании функций-обёрток:

def my_print(*args, **kwargs): flush = kwargs.pop('flush', True) # flush unless overriden print(*args, flush=flush, **kwargs) 

При множественном наследовании **kwargs помогает реализовать требование совместимости параметров для методов базовых классов, так как kwargs позволяет передать произвольные именованные аргументы.

Видно, что звёздочку можно использовать и при вызове функции:

L = [1, 2, 3] s = "abc" print(*L, *s) # iterable unpacking: print(1, 2, 3, 'a', 'b', 'c') # -> 1 2 3 a b c 

произвольные коллекции (iterable в общем случае) L , s распаковываются и каждый их элемент передаётся в виде отдельного агрумента в вызываемую функцию ( print() ).

Можно использовать и при явном присваивании:

>>> first, *middle, last = L >>> first, middle, last (1, [2], 3) 

в этом случае первый и последний аргументы из списка L распаковываются в явно приведённые имена ( first , last ), а остаток ноль или более элементов в виде списка помещаются в middle .

Звёздочку можно использовать и при задании списков, кортежей, наборов и словарей в исходном коде, используя соответствующий синтаксис (tuple, list, set, and dictionary displays):

>>> *range(4), 4 (0, 1, 2, 3, 4) >>> [*range(4), 4] [0, 1, 2, 3, 4] >>>  >>> > # dictionary unpacking inside dictionary display

Тонкий момент: запятая в Питоне создаёт кортеж—скобки нужны только для пустого кортежа () . Поэтому первая строчка равнозначна: (*range(4), 4) .

Так же как и при вызове функций, звёздочка распаковывает коллекцию здесь и действует как будто каждый элемент был передан отдельно в соответствующие конструкторы.

Таким образом можно сложить два словаря или произвольные отображения (Mapping):

>>> a = >>> b = >>> <**a, **b> >>>

При наличии дублирующих ключей, более поздние значения побеждают, как обычно: == .

Знание, что делает звёздочка полезно, чтобы объяснить как zip(*matrix) транспонирует квадратную матрицу или как обойти итератор по ровно n элементов за раз: zip(*[iterator]*n) .

Помимо указанных значений, звёздочка может присутствовать в имени файла для создания шаблона (wildcard), к примеру:

from pathlib import Path print(*Path().glob('*.py')) print(*Path().glob('**/*.py')) 

Первый print() печатает через пробел все (ноль или более) имена файлов в текущей директории с расширением .py . Подобный ( *.py ) синтаксис популярен, так как он используется в командной строке (shell).
Второй print() с двумя звёздочками ( ‘**/*.py’ ) выводит имена Питон-файлов во всем дереве директорий (включая вложенные директории).

В регулярных выражениях * означает повторение ноль или более раз:

import re if re.fullmatch(r'x*', text): print('текст пустой или содержит только `x`') 

† умножение натуральных чисел n*m можно рассматривать как повторение сложения (ноль или более) раз. Аналогично, возведение в степень n**m можно рассматривать как повторение умножения:

 2 * 3 == 2 + 2 + 2 2 ** 3 == 2 * 2 * 2 [2] * 3 == [2, 2, 2] 

Отслеживать
ответ дан 22 ноя 2016 в 21:07
52.2k 11 11 золотых знаков 108 108 серебряных знаков 311 311 бронзовых знаков
<**a, **b>не работает в питоне 3.4. Укажите что с какой версии работает.
10 фев 2017 в 10:01

@SmitJohnth можно, но и так много информации в ответе. Текущая версия Python уже 3.6 Кого интересуют подробности, могут на ссылку нажать — там версия приведена (я для того ссылки и привожу, чтобы у кого желание есть, могли больше деталей получить).

10 фев 2017 в 10:22
Что, 2.7 уже закопали?
10 фев 2017 в 14:21

@SmitJohnth у вопроса есть метка python-3.x и нет метки python-2.7 Если есть желание, вы можете аналогичный вопрос для Питона 2 спросить (отличий много, ответ и так много материала по самым верхам покрывает).

10 фев 2017 в 23:20

@SmitJohnth посмотрите на автора вопроса. Ещё раз, если вас что-то другое интересует, то задайте свой вопрос (я уверен, что мне известны намерения автора текущего вопроса).

14 фев 2017 в 15:18

Звездочка (*) в Питоне используется для разбора аргументов. Она указывает, что передаваемый объект должен быть разобран (распакован) в отдельные аргументы.

В первом примере кода *min(p for p in counter.items() if p[1] == max_count) разбирается в аргументы и передается в функцию print.

Во втором примере переменные team и coef разбираются в отдельные аргументы и передаются в функцию print .

В третьем примере *args разбирается в отдельные аргументы и передается в функцию datetime .

В четвертом примере *args разбирается в отдельные аргументы и передается в функцию reduce.

В пятом примере *args разбирается в отдельные аргументы и передается в функцию watchdog.

Две звездочки (**) также используются для разбора аргументов, но в этом случае объект разбирается в словарь из именованных аргументов.

В первом примере ‘f>’.format(**vars()) звездочки указывают, что vars() должен быть разобран в словарь из именованных аргументов, которые затем используются в функции format .

Во втором примере в классе A в методе init **kwargs разбирается в словарь из именованных аргументов, которые затем передаются в вызов функции super().init .

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

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

def some_function(**kwargs): print(kwargs) some_function(arg1=1, arg2=2, arg3=3) 

В этом примере функция some_function ожидает передачу именованных аргументов. Когда мы вызываем функцию some_function с аргументами arg1=1, arg2=2, arg3=3 , они разбираются в словарь из именованных аргументов и передаются в функцию some_function . В результате вызова функции будет выведено .

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

Давайте рассмотрим еще несколько примеров использования разбора аргументов:

def some_function(arg1, arg2, *args): print(arg1, arg2, args) some_function(1, 2, 3, 4, 5) 

В этом примере функция some_function ожидает передачу двух обязательных аргументов arg1 и arg2 , а также любого количества дополнительных аргументов. Когда мы вызываем функцию some_function с аргументами 1, 2, 3, 4, 5 , то первые два аргумента присваиваются arg1 и arg2 соответственно, а остальные три аргумента разбираются в кортеж args. В результате вызова функции будет выведено 1, 2, (3, 4, 5) .

Еще один пример:

def some_function(arg1, **kwargs): print(arg1, kwargs) some_function(1, arg2=2, arg3=3) 

В этом примере функция some_function ожидает передачу одного обязательного аргумента arg1 и любого количества дополнительных именованных аргументов. Когда мы вызываем функцию some_function с аргуменентами 1, arg2=2, arg3=3 , то первый аргумент присваивается arg1, а остальные аргументы разбираются в словарь kwargs . В результате вызова функции будет выведено 1, .

Еще один пример:

def some_function(arg1, *args, **kwargs): print(arg1, args, kwargs) some_function(1, 2, 3, arg4=4, arg5=5) 

В этом примере функция some_function ожидает передачу одного обязательного аргумента arg1 , любого количества дополнительных аргументов и любого количества дополнительных именованных аргументов. Когда мы вызываем функцию some_function с аргументами 1, 2, 3, arg4=4, arg5=5 , то первый аргумент присваивается arg1 , второй и третий аргументы разбираются в кортеж args , а остальные аргументы разбираются в словарь kwargs . В результате вызова функции будет выведено 1, (2, 3), .

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

def average_filter(numbers): avg = sum(numbers) / len(numbers) return [x for x in numbers if x > avg] print(average_filter([1, 2, 3, 4, 5])) 

В этом примере функция average_filter принимает на вход список чисел и вычисляет среднее значение списка. Затем функция использует генератор списка, чтобы создать список элементов, которые больше среднего значения. В результате вызова функции со списком [1, 2, 3, 4, 5] будет возвращен список [4, 5] .

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

def sort_by_length(strings): return sorted(strings, key=lambda s: len(s), reverse=True) print(sort_by_length(['abc', 'def', 'gh', 'ijkl'])) 

В этом примере функция sort_by_length использует генератор списка sorted и лямбда-функцию, чтобы отсортировать список строк в порядке убывания длины. В результате вызова функции со списком [‘abc’, ‘def’, ‘gh’, ‘ijkl’] будет возвращен список [‘ijkl’, ‘abc’, ‘def’, ‘gh’] .

Один из примеров использования разбора аргументов с звездочками в сочетании с лямбда-функциями может быть реализация функции, которая принимает на вход список чисел и возвращает список элементов, которые делятся на 3 без остатка.

def multiple_of_3(numbers): return list(filter(lambda x: x % 3 == 0, numbers)) print(multiple_of_3([1, 2, 3, 4, 5, 6, 7, 8, 9])) 

В этом примере функция multiple_of_3 использует функцию filter и лямбда-функцию, чтобы отфильтровать список чисел и вернуть список элементов, которые делятся на 3 без остатка

Супер-звезда Python. 5 способов использования оператора *.

Символ звездочка ( * ) или астериск, известный как оператор умножения, часто используется в языках программирования. Новичкам достаточно знать его основное применение.

Однако, если Вы собираетесь стать экспертом по Python, то нужно углубить свои знания! Пришло время узнать, насколько полезна и мощна “звездочка” в Python.

В этой статье мы рассмотрим 5 способов использования оператора * с примерами, от элементарных к более сложным.

Способ 1: Умножить или возвести в степень

Самое очевидное — использовать звездочки как математические операторы:

  • * — операция умножения.
  • ** — операция возведения в степень.
 
2*3 # 6 2**3 # 9

Способ 2: Любое количество аргументов функции

Функция не обязательно должна получать фиксированное количество аргументов.

Предположим, что Вы заранее не знаете, сколько аргументов будет передано. Здесь «звездочки» могут прийти к Вам на помощь!

 
def print_genius(*names): print(type(names)) for n in names: print(n) print_genius('Elon Mask', 'Mark Zuckerberg ', 'Yang Zhou') # class 'tuple' # Elon Mask # Mark Zuckerberg # Yang Zhou def top_genius(**names): print(type(names)) for k, v in names.items(): print(k, v) top_genius(Top1="Elon Mask", Top2="Mark Zuckerberg", Top3="Yang Zhou") # class 'dict' # Top1 Elon Mask # Top2 Mark Zuckerberg # Top3 Yang Zhou

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

  • Параметр с префиксом * может записывать любое количество позиционных аргументов в tuple из аргументов
  • Параметр с префиксом ** может записывать любое количество именованных аргументов и составить из них словарь dict

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

 
def func(*args, **kwargs): pass

Вообще говоря, вместо args и kwargs может стоять все, что угодно — это просто общепринятое соглашение. Можете написать tea и coffee . Но понятней от этого не станет 🙂

Способ 3: Контроль за именованными аргументами

Еще одно полезное использование звездочек — заставить функцию принимать только именованные аргументы.

 
def genius(*, first_name, last_name): print(first_name, last_name) genius('Yang','Zhou') # TypeError: genius() takes 0 positional arguments but 2 were given genius(first_name='Yang', last_name='Zhou') # Yang Zhou

Как показано в приведенном выше примере, одна звездочка * вводит ограничение: все последующие аргументы должны быть именованными.

Если нам нужно ввести ограничение на несколько именованных аргументов и оставить некоторые аргументы позиционными, то нужно просто поставить позиционные аргументы перед звездочкой:

 
def genius(age, *, first_name, last_name): print(first_name, last_name, 'is', age) genius(28, first_name='Yang', last_name='Zhou') # Yang Zhou is 28

В данном примере, если бы Вы ввели ‘Yang’ без ключевого слова first_name , то Вы получили бы ошибку.

Ситуация 4: Распаковка объектов

Мы можем использовать звездочки для распаковки кортежей, списков, словарей и других итерируемых объектов, что сделает наши программы понятными и элегантными.

Например, если мы собираемся объединить элементы разных итерируемых объектов, например, один список, один кортеж и один набор в новый список, что нам делать?

Очевидно, мы можем использовать циклы for для перебора всех элементов и добавления их в новый список один за другим:

 
A = [1, 2, 3] B = (4, 5, 6) C = 7, 8, 9> L = [] for a in A: L.append(a) for b in B: L.append(b) for c in C: L.append(c) print(L) # [1, 2, 3, 4, 5, 6, 8, 9, 7]

Таким образом можно выполнить нашу миссию, но код выглядит таким длинным и не очень оптимизированным. Лучший метод — использовать генератор списков:

 
A = [1, 2, 3] B = (4, 5, 6) C = 7, 8, 9> L = [a for a in A] + [b for b in B] + [c for c in C] print(L) # [1, 2, 3, 4, 5, 6, 8, 9, 7]

Мы сократили три цикла for до одной строки. Это уже хороший код, но способ не самый простой! Можно еще красивей.

Пора посмотреть, насколько красивы в данной ситуации звездочки 🙂

 
A = [1, 2, 3] B = (4, 5, 6) C = 7, 8, 9> L = [*A, *B, *C] print(L) # [1, 2, 3, 4, 5, 6, 8, 9, 7]

Как показано в примере выше, мы можем использовать звездочку в качестве префикса объектов для распаковки их элементов. Кстати, если мы используем * в качестве префикса к словарю, то будут распакованы его ключи. Если мы используем двойные звездочки ** в качестве префикса, то будут распакованы его значения. Однако, мы должны использовать ключи для получения распакованных значений. Из-за этого неудобства не принято извлекать элементы из словаря помощью звездочек.

 
D = 'first': 1, 'second': 2, 'third': 3> print(*D) # first second third # print(**D) # TypeError: 'first' is an invalid keyword argument for print() print(',,'.format(**D)) # 1,2,3

Ситуация 5: Множественное присваивание

Этот синтаксис распаковки был введен в PEP 3132, чтобы сделать наш код более элегантным.

Этот PEP позволяет указать «всеобъемлющее» имя, в который будет помещен весь список элементов, которым не присвоено «обычное» имя.

 
L = [1, 2, 3, 4, 5, 6, 7, 8] a, *b = L print(a) # 1 print(b) # [2, 3, 4, 5, 6, 7, 8]

Эпилог

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

Что значит 2 звездочки в питоне

Звездочка (*), известная как оператор умножения, — широко используемый символ во всех программах. В большинстве случаев нам достаточно использовать его просто как оператор умножения. Однако, если вы серьезно собираетесь стать экспертом по Python-у, то для вас пришло время узнать, насколько полезна и мощна звездочка в Python-е.

Этот статья покажет 5 примеров использования звездочек с достаточно понятными примерами, от самых элементарных до продвинутых.

Применение 1: Оператор умножения или возведения в степень

Самое простое применение — использовать звездочку в качестве инфиксного оператора:

  • Одна звёздочка * — для операции умножения.
  • Двойная ** — для операции возведения в степень.
 >>> 2*3 >>> 6 >>> 2**3 >>> 8 
Применение 2: Получение неограниченного количества аргументов

Функция не обязательно должна получать фиксированное количество аргументов. Если вам нужна большая гибкость или вы не уверены, сколько аргументов будет передано в функцию, вы можете передать в неё неограниченное количество аргументов. Время шоу звездочек.

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

  • Параметр с одним префиксом * может захватывать любое количество позиционных аргументов в кортеж.
  • Параметр с двойным префиксом ** может захватывать любое количество ключевых аргументов в словарь.

Условно мы определяем функцию следующим образом, если число ее аргументов может быть не определено:

 def func(*args, **kwargs): pass 
Применение 3: Использование ключевых аргументов функции

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

Пример говорит больше тысячи слов:

Как показано в приведенном выше примере, единственная звёздочка * ограничивает все следующие аргументы; они должны быть переданы в качестве ключевых аргументов.

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

Применение 4: Итеративная распаковка

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

Например, если мы собираемся скомбинировать различные итерируемые объекты, например, список, кортеж и множество в новый список, то как лучше это сделать?

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

Мы можем таким образом выполнить нашу миссию, но код выглядит очень длинным и не очень “питонистским”.

Более лучшим методом является использование list comprehensions:

Мы уменьшили цикл с трёх строчек, записав его в виде одной строки. Это уже в стиле Python, но это не самый простой способ!

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

Как было сказано выше, мы можем использовать звездочку как префикса для итерируемых объектов для распаковки их элементов.

Кстати, если мы используем одну звёздочку * в качестве префикса словаря, его ключи будут распакованы. Если мы используем двойные звездочки ** в качестве префикса, будут распакованы его значения. Однако мы обязаны использовать ключи, для распаковывания значений. Из-за этого неудобства не принято извлекать элементы словаря звездочками.

Применение 5: Расширенная итеративная распаковка

Этот синтаксис распаковки был введен PEP 3132, чтобы сделать наш код более элегантным.

Этот PEP предлагает изменение синтаксиса итеративной распаковки, позволяя указать «перехватывающее» имя, которое будет присвоено всем элементам списка, не связанных с “обычным” именем.

Вывод

Звездочка — один из наиболее часто используемых операторов в программах. Помимо использования в качестве оператора умножения, в Python существует несколько элегантных и мощных его применений, которые помогут нашему коду стать более “питонистическим”.

Операторы * и ** в Питоне. Что это и как использовать

Операторы (operators) * и ** встречаются в питоне очень часто. Иногда они немного непонятны и новичкам, и опытным ребятам, переходящим на питон с ряда других языков программирования (в которых операторы могут использоваться немного иначе). Статья известного питон-коуча Трея Ханнера (Trey Hunner), который помогает девелоперам расширять свои знания. Дата написания статьи: 10.11.2018.

Функционал операторов * и ** развивается уже много лет. Я хочу рассмотреть все способы их использования по состоянию на текущий момент. Буду указывать, что конкретно работает только в современных версиях питона. Поэтому, если вы изучали операторы * и ** еще во времена питона 2 (Python 2), советую хотя бы проглядеть данную статью, потому что в питоне 3 (Python 3) этим операторам добавили много новых возможностей.

Если вы начали изучать питон недавно и еще не освоили аргументы ключевых слов (keyword arguments; также известные как именованные аргументы, named arguments), предлагаю сперва прочитать мою статью про аргументы ключевых слов в питоне.

Что мы обсуждать не будем

В данной статье, говоря про операторы * и **, я имею в виду операторы-префиксы (prefix operators), а не инфиксы (infix). То есть, функции умножения и возведения в степень не входят в тему статьи.

Тогда про что же мы говорим

Мы говорим про операторы-префиксы * и **, которые используются перед переменной (variable). Например:

>>> numbers = [2, 1, 3, 4, 7]
>>> more_numbers = [*numbers, 11, 18]
>>> print(*more_numbers, sep=’, ‘)
2, 1, 3, 4, 7, 11, 18

В данном коде можно увидеть два способа использования оператора *. Оператор ** отсутствует.

В сферу применения рассматриваемых операторов входит:
1. Операторы * и **: передача аргументов в функцию.
2. Операторы * и **: захват аргументов, переданных в функцию.
3. Оператор *: принятие аргументов, содержащих только ключевые слова.
4. Оператор *: захват элементов во время распаковки кортежа (tuple).
5. Оператор *: распаковка итерируемых объектов в списке или кортеже.
6. Оператор **: + распаковка словарей в других словарях.

Даже если вам кажется, что вы освоили все эти способы, позволяющие использовать операторы * и **, рекомендую посмотреть на все нижеприведенные блоки кода, чтобы убедиться, что они все вам знакомы. Последние несколько лет разработчики ядра питона продолжают добавлять этим операторам новые возможности, поэтому какие-то из новых применений можно проглядеть.

Операторы * и ** при распаковке во время вызова функции

При вызове функции оператор * можно задействовать для распаковки итерируемого объекта в аргументах, введенных для вызова:

>>> fruits = [‘lemon’, ‘pear’, ‘watermelon’, ‘tomato’]
>>> print(fruits[0], fruits[1], fruits[2], fruits[3])
lemon pear watermelon tomato
>>> print(*fruits)
lemon pear watermelon tomato

В строке print(*fruits) производится вызов всех элементов списка fruits в функции print. Они становятся отдельными аргументами. При этом нам даже не надо знать, сколько всего аргументов окажется в списке.

В данном примере оператор * – не просто синтактический выверт (syntactic sugar). Без * отправить все элементы конкретного итерируемого объекта в качестве отдельных аргументов было бы невозможно (это не касается списков с сфиксированной длиной).

Еще один пример:

def transpose_list(list_of_lists):
return [
list(row)
for row in zip(*list_of_lists)
]

В данном случае мы принимаем список со списками в качестве элементов и возвращаем «преобразованный» список со списками:

>>> transpose_list([[1, 4, 7], [2, 5, 8], [3, 6, 9]])
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Функционал оператора ** примерно такой же, просто он применяется к аргументам ключевых слов. Он позволяет нам взять словарь, содержащий пары из ключей и значений, и распаковать его в аргументы ключевых слов при вызове функции.

>>> date_info = <'year': "2020", 'month': "01", 'day': "01">
>>> filename = «—.txt».format(**date_info)
>>> filename
‘2020-01-01.txt’

Скажу из своего опыта. Оператор ** не часто используется для распаковки аргументов ключевых слов при вызове функции. Чаще всего я вижу такие примеры при работе с наследованием: вызовы super() часто включают в себя оба оператора.

Операторы * и ** можно использовать неоднократно при вызове функции. Данная возможность появилась в питоне 3.5. Иногда это может оказаться очень уместным:

>>> fruits = [‘lemon’, ‘pear’, ‘watermelon’, ‘tomato’]
>>> numbers = [2, 1, 3, 4, 7]
>>> print(*numbers, *fruits)
2 1 3 4 7 lemon pear watermelon tomato

Неоднократное использование ** выглядит примерно так же:

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

Операторы * и ** при упаковке аргументов, переданных функции

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

from random import randint
def roll(*dice):
return sum(randint(1, die) for die in dice)

Данная функция принимает любое количество аргументов.

>>> roll(20)
18
>>> roll(6, 6)
9
>>> roll(6, 6, 6)
8

Функции питона print и zip принимают любое количество позиционных аргументов. Такое использование оператора * при упаковке аргументов позволяет нам создавать свои функции, которые (аналогично print и zip) принимают любое количество аргументов.

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

def tag(tag_name, **attributes):
attribute_list = [
f’=»»‘
for name, value in attributes.items()
]
return f» >»

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

>>> tag(‘a’, href=»http://treyhunner.com»)
>>> tag(‘img’, height=20, width=40, src=»https://kirill-sklyarenko.ru/face.jpg»)

Позиционные аргументы, содержащие аргументы только из ключевых слов

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

Чтобы принимать аргументы, содержащие только ключевые слова, мы можем поместить именованные аргументы после оператора * при определении тела функции:

def get_multiple(*keys, dictionary, default=None):
return [
dictionary.get(key, default)
for key in keys
]

Данную функцию можно использовать так:

Аргументы dictionary и default поставлены после *keys. То есть, их можно только в качестве аргументов ключевых слов. Если мы попытаемся определить их позиционно, то увидим ошибку:

>>> fruits = <'lemon': 'yellow', 'orange': 'orange', 'tomato': 'red'>
>>> get_multiple(‘lemon’, ‘tomato’, ‘squash’, fruits, ‘unknown’)
Traceback (most recent call last):
+ File «», line 1, in
TypeError: get_multiple() missing 1 required keyword-only argument: ‘dictionary’

Данное поведение внедрено в питон с помощью предложения PEP 3102.

Аргументы, содержащие только ключевые слова и не содержащие позиционные аргументы

Аргументы, содержащие только ключевые слова, – неплохое средство. Но что если вы хотите требовать ввода аргументов, содержащих только ключевые слова, не захватывая неограниченное количество позиционных аргументов?

питон позволяет сделать это с помощью немного странного синтаксиса, когда оператор * как бы сам по себе:

def with_previous(iterable, *, fillvalue=None):
«»»Yield each iterable item along with the item before it.»»»
previous = fillvalue
for item in iterable:
yield previous, item
previous = item

Данная функция принимает аргумент, содержащий итерируемый объект (iterable). Его можно определить позиционно (то есть, первым) или с помощью названия и аргумента fillvalue, который входит в число аргументов, допускающих только ключевые слова. Это означает, что мы можем вызвать функцию with_previous вот так:

>>> list(with_previous([2, 1, 3], fillvalue=0))
[(0, 2), (2, 1), (1, 3)]

>>> list(with_previous([2, 1, 3], 0))
Traceback (most recent call last):
File «», line 1, in
TypeError: with_previous() takes 1 positional argument but 2 were given

Данная функция принимает два аргумента. Один из них, fillvalue, обязательно определяется как аргумент, содержащий ключевое слово.

Обычно я использую аргументы, допускающие только ключевые слова, при захвате неопределенного количества позиционных аргументов. Но иногда я использую данную возможность оператора *, чтобы форсировать исключительно позиционное определение аргумента.

На самом деле, данный подход используется встроенной функцией питона sorted. Если посмотреть справку для sorted, можно увидеть следующее:

>>> help(sorted)
Help on built-in function sorted in module builtins:
sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order.

Пример использования оператора * как самого по себе прямо в документации по аргументам функции sorted.

Операторы * и ** при распаковке кортежа

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

Оператор * теперь можно использовать при распаковке кортежа:

>>> fruits = [‘lemon’, ‘pear’, ‘watermelon’, ‘tomato’]
>>> first, second, *remaining = fruits
>>> remaining
[‘watermelon’, ‘tomato’]
>>> first, *remaining = fruits
>>> remaining
[‘pear’, ‘watermelon’, ‘tomato’]
>>> first, *middle, last = fruits
>>> middle
[‘pear’, ‘watermelon’]

Если вы задаете себе вопрос: как же мне использовать это в своем коде, посмотрите примеры в моей статье про распаковку кортежей в питоне. В этой статье я показал, каким образом такое использование оператора * может, в некоторых случаях, стать альтернативой для срезания последовательностей (sequence slicing).

Обычно во время своих лекций об операторе * я говорю, что можно использовать только одно выражение с ним в отдельном вызове многократного присвоения (multiple assignment). Технически это некорректно, потому что можно его использовать два раза при вложенной распаковке (nested unpacking). Данный вопрос я рассмотрел подробно в статье про распаковку кортежей.

>>> fruits = [‘lemon’, ‘pear’, ‘watermelon’, ‘tomato’]
>>> ((first_letter, *remaining), *other_fruits) = fruits
>>> remaining
[‘e’, ‘m’, ‘o’, ‘n’]
>>> other_fruits
[‘pear’, ‘watermelon’, ‘tomato’]

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

Данная возможность добавлена в питон на основе предложения PEP 3132. Следует отметить, что оно не относится к очень длинным.

Операторы * и ** в литерале списка

В питоне 3.5 добавлено очень много возможностей, связанных с оператором *, на основе предложения PEP 448. Одной из самых заметных новых возможностей стало использование * для вывода итерируемого объекта в новый список.

Допустим, у вас есть функция, которая принимает любые последовательности и возвращает список, содержащий каскад из последовательности и ее реверсивного варианта:

def palindromify(sequence):
return list(sequence) + list(reversed(sequence))

Данная функция должна пару раз провести конвертацию в список, чтобы объединить списки и вернуть результат. Начиная с питона 3.5, мы можем, вместо вышеприведенного примера, написать следующее:

def palindromify(sequence):
return [*sequence, *reversed(sequence)]

В данном коде больше нет нескольких ненужных вызовов списков. Поэтому он стал эффективнее и лучше читается.

def rotate_first_item(sequence):
return [*sequence[1:], sequence[0]]

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

Это очень удачная возможность, позволяющая с помощью оператора * объединять итерируемые объекты различных типов. Оператор * работает с любыми итерируемыми объектами, а оператор + работает только с определенными последовательностями, при чем все из объединяемых должны быть одного типа.

Отмечу, что данная возможность не ограничивается только созданием списков. Мы можем выводить итерируемые объекты в новые кортежи или множества (set):

Обратите внимание, что последняя строка принимает список и генератор (generator), а потом выводит их в новое множество. Перед появлением этой возможности для оператора * было непросто сделать это в одну строку кода. Разумеется, способ сделать это существовал, но его было непросто вспомнить или обнаружить:

>>> set().union(fruits, uppercase_fruits)

Оператор ** в литерале словаря

Помимо вышеприведенного на основе предложения PEP 448 в функционал ** добавлен вывод пар ключ/значение (key/value) из словаря в новый словарь:

>>> date_info =
>>> track_info =
>>> all_info = <**date_info, **track_info>
>>> all_info

Про это я написал еще одну статью. Сейчас ее можно найти под новым названием про идиоматический способ сливать словари в питоне. Данную возможность можно использовать не только для сливания двух словарей. Например, можно скопировать словарь, параллельно добавляя в него новые значения:

>>> date_info =
>>> event_info = <**date_info, 'group': "Python Meetup">
>>> event_info

Еще можно скопировать или слить словари, переписывая определенные значения:

>>> event_info =
>>> new_info = <**event_info, 'day': "14">
>>> new_info

Операторы * и ** обладают немалыми возможностями в питоне

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

Прочитав обо всех возможностях * и **, вы, возможно, удивитесь названиям, под которыми используются эти странные операторы. К сожалению, для них нет лаконичных названий. Я слышал, как * называли оператором для упаковки и распаковки. Еще слышал, как его называли «splat» (это из мира Руби) и просто звездой.

Чаще всего я называю их звездой (star) и двойной звездой (double star) (или звездой-звездой (star star)). В данном случае разделения с их функциями как инфиксов не проводится (речь идет про операции умножения и возведения в степень). Но обычно из контекста очевидно, о чем идет речь, про префиксы или инфиксы.

Если вы не понимаете операторы * и ** или боитесь не запомнить все их возможности, не нужно беспокоиться. Способов использования много, и запоминать каждую конкретную возможность для каждого из них не так важно. Лучше будет осознать, в каких случаях к ним можно обратиться. Предлагать использовать данную статью как чек-лист или создать свой чек-лист, который поможет вам использовать операторы * и ** в питоне.

  • перевод с английского
  • python
  • операторы

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *