Функции (def) в Python: создать, вызвать, аргументы, *args и **kwargs
Использование args и kwargs может быть довольно загадочным, особенно если вы новичок в Python. Давайте вместе узнаем все, что вам нужно о них.
Что такое * args и ** kwargs?
* args и ** kwargs позволяют передавать произвольное количество позиционных аргументов (* args) и аргументов ключевого слова (** kwargs) в функцию Python. Вы можете использовать * args и ** kwargs, чтобы сделать ваш код Python более гибким.
Мы начнем с простого примера, который быстро покажет вам, как использовать * args и ** kwargs. Затем мы рассмотрим другие примеры, чтобы убедиться, что вы их полностью понимаете.
Мы также увидим, где еще можно использовать * args и ** kwargs…
Давайте начнем!
Пример использования * args с функцией
Давайте создадим простую функцию, которая вычисляет сумму двух чисел:
def sum(x, y):
result = x + y
return result
Затем мы вызовем его с одним, двумя или тремя аргументами, чтобы увидеть, что произойдет:
Один аргумент
print(sum(1))
Мы возвращаем следующую ошибку, потому что функция суммы ожидает два аргумента, а мы передали только один:
Traceback (most recent call last):
File «/opt/python/codefather/args_kwargs.py», line 25, in
print(sum(1))
TypeError: sum() missing 1 required positional argument: 'y'
- Обратите внимание, как интерпретатор Python сообщает нам, что отсутствует 1 обязательный позиционный аргумент (y).
- Скоро вы увидите, как позиционные аргументы связаны с * args…
- Два аргумента
print(sum(1, 2))
На этот раз ошибки нет. Наша программа выводит правильную сумму, потому что мы передаем правильное количество аргументов при вызове функции суммы.
Ваши аргументы
print(sum(1, 2, 3))
Мы возвращаем ошибку, потому что наша функция не знает, как обрабатывать три аргумента:
Traceback (most recent call last):
File «/opt/python/codefather/args_kwargs.py», line 25, in
print(sum(1, 2, 3))
TypeError: sum() takes 2 positional arguments but 3 were given
Мы можем решить эту проблему с помощью * args…
Что такое * args?
* args позволяет передавать переменное количество позиционных аргументов в функцию Python. Он доступен как кортеж в самой функции.
Давайте внесем несколько изменений в нашу функцию суммы:
- Добавьте * args в качестве последнего аргумента в сигнатуре функции.
- Убедитесь, что args пуст, и если он не пустой, добавьте каждый аргумент в * args к окончательному результату.
def sum(x, y, *args):
print(«args value:», args)
result = x + y
if args:
for arg in args:
result += arg
return result
Когда мы выполняем этот код, мы получаем результат 6, и мы также можем видеть содержимое кортежа * args:
>>> print(sum(1, 2, 3))
args value: (3,)
6
Кортежи похожи на списки Python с той лишь разницей, что они неизменяемы.
Как передать ** kwargs в функцию
Теперь, когда мы увидели, как работает * args, перейдем к ** kwargs.
Что такое ** kwargs?
** kwargs позволяет передавать переменное количество аргументов ключевого слова функции Python. Его содержание доступно в виде словаря в самой функции.
Давайте посмотрим, как это применимо к нашей функции суммы …
def sum(x, y, *args, **kwargs):
print(«args value:», args)
print(«kwargs value:», kwargs)
result = x + y
if args:
for arg in args:
result += arg
if kwargs:
for kwarg in kwargs.values():
result += kwarg
return result
Мы обновили функцию суммы, чтобы:
- Примите параметр ** kwargs.
- Распечатайте содержимое словаря kwargs.
- К окончательной сумме прибавьте любые значения в ** kwargs.
Когда мы вызываем функцию:
print(sum(1, 2, 3, number1=6, number2=8))
Результат следующий:
args value: (3,)
kwargs value: {'number1': 6, 'number2': 8}
20
- Вы можете увидеть содержимое словаря kwargs и сумму всех чисел, равную 20.
- Вы видите, как работает ** kwargs?
- Теперь…
Чтобы сделать функцию суммы универсальной, мы могли бы удалить x и y и просто сохранить * args и ** kwargs в качестве аргументов функции. Попробуйте!
Я включу полный исходный код в конце этого руководства.
А теперь перейдем к другому…
Зачем нужны имена * args и ** kwargs
- На основании того, что мы уже видели, вы можете подумать, что интерпретатор Python точно распознает имена * args и ** kwargs.
- Давайте проведем небольшой эксперимент…
- Я изменил нашу функцию суммы, вы видите разницу?
def sum(x, y, *custom_args, **custom_kwargs):
print(«args value:», custom_args)
print(«kwargs value:», custom_kwargs)
result = x + y
if custom_args:
for arg in custom_args:
result += arg
if custom_kwargs:
for kwarg in custom_kwargs.values():
result += kwarg
return result
- Я изменил * args и ** kwargs соответственно на * custom_args и ** custom_kwargs.
- Запустите новую программу, вы увидите, что функция по-прежнему работает так же, как и раньше.
- Итак, похоже, имена args и kwargs на самом деле не имеют значения…
- … Так как интерпретатор Python знает, что делать с * custom_args и ** custom_kwargs?
- Применим еще одно небольшое изменение…
def sum(x, y, args, kwargs):
…
…
Я удалил * и ** из args и kwargs. И вывод становится:
Traceback (most recent call last):
File «/opt/python/codefather/args_kwargs.py», line 37, in
print(sum(1, 2, 3, number1=6, number2=8))
TypeError: sum() got an unexpected keyword argument 'number1'
Это показывает, что…
Интерпретатор Python понимает, как обрабатывать * args и ** kwargs, — это просто * и ** перед именами параметров. Вот почему вы можете использовать любые имена, какие захотите. Использование имен args и kwargs — это просто соглашение, упрощающее чтение кода для всех разработчиков.
* называется итеративным оператором распаковки, а ** — оператором распаковки словаря.
Позже в этом руководстве мы более подробно рассмотрим, что означает, что * и ** являются операторами распаковки …
Передача * args и ** kwargs функции
Сейчас мы попробуем что-нибудь другое, чтобы показать, почему мы говорим, что * args содержит позиционные аргументы, а ** kwargs содержит аргументы ключевого слова.
Вернемся к первой версии нашей функции суммы:
def sum(x, y):
result = x + y
return result
- На этот раз мы хотим использовать * args и ** kwargs для передачи аргументов этой функции.
- Вот как…
- Передача аргументов с использованием * args
- Мы определим кортеж, содержащий значения x и y. Затем мы передадим его как * args:
args = (1, 2)
print(sum(*args))
Попробуйте и убедитесь, что результат правильный.
В этом случае значения в кортеже * args присваиваются аргументам x и y в зависимости от их положения. Первому элементу в кортеже присваивается x, а второму элементу y.
Если мы добавим пару операторов печати к функции суммы, чтобы увидеть значения x и y…
def sum(x, y):
print(«x:», x)
print(«y:», y)
result = x + y
return result
… Получаем следующий результат:
x: 1
y: 2
3
Передача аргументов с помощью ** kwargs
Мы определим словарь, содержащий x и y в качестве ключей. Чтобы показать, что в этом случае позиция (или порядок) элемента словаря не имеет значения, мы укажем ключ y перед ключом x.
Затем мы передадим его как ** kwargs в нашу функцию суммы:
kwargs = {'y': 2, 'x': 1}
print(sum(**kwargs))
Если вы выполните код, вы снова увидите, что результат правильный.
Значения в словаре ** kwargs присваиваются аргументам x и y на основе их имени. Значение словарного ключа «x» присваивается параметру x функции, и то же самое относится к y.
Два оператора печати, которые мы добавили к функции ранее, подтверждают, что значения x и y являются теми, которые мы ожидаем:
x: 1
y: 2
3
Вы можете видеть, как ** kwargs может быть довольно удобным способом передачи аргументов функции после создания словаря, содержащего необходимые аргументы.
Что такое тип для * args и * kwargs?
Мы говорили о том, что * args — это кортеж, а ** kwargs — это словарь.
Но сначала давайте не будем принимать это как должное …
… Мы будем использовать функцию Python type () для проверки их типа.
def print_args_type(*args, **kwargs):
print(«*args type:», type(args))
print(«**kwargs type:», type(kwargs))
print_args_type(1, 3, number=5)
Вот результат:
*args type:
**kwargs type:
Итак, то, что мы видели до сих пор, подтверждается, когда речь идет о типах * args и ** kwargs.
А теперь давайте посмотрим, что может вызвать синтаксические ошибки в вашем коде Python …
Порядок аргументов для функции Python
При использовании * args и ** kwargs вместе со стандартными позиционными аргументами или аргументами ключевого слова вам нужно знать кое-что …
Чтобы продемонстрировать эту концепцию, я изменил функцию, которую мы создали в предыдущем разделе:
def print_args_type(*args, **kwargs, arg1):
print(«*args type:», type(args))
print(«**kwargs type:», type(kwargs))
print(«arg1:», arg1)
print_args_type(1, 3, number=5, 6)
Вот изменения, которые я внес в нашу программу:
- arg1 добавлен в конец списка параметров функции print_args_type ().
- В функцию добавлен оператор print для печати значения arg1.
- дополнительный аргумент (6) передается, когда мы вызываем print_args_type ().
Теоретически мы ожидаем, что новый аргумент, переданный при вызове функции, будет назначен параметру arg1, но это то, что мы получаем обратно:
File «/opt/python/codefather/args_kwargs.py», line 48
def print_args_type(*args, **kwargs, arg1):
^
SyntaxError: invalid syntax
- Почему-то интерпретатору Python не нравится arg1.
- Но почему?
- Это потому, что Python обеспечивает определенный порядок аргументов функции:
- Позиционные аргументы идут первыми в следующем порядке: стандартные аргументы, за которыми следует * args.
- Аргументы ключевого слова должны идти после позиционных аргументов в следующем порядке: стандартные аргументы, за которыми следует ** kwargs.
Это означает, что arg1 должен стоять перед * args.
Посмотрим, правда ли это, вот обновленная функция. Обратите внимание, как я также изменил положение аргумента 6 при вызове функции:
def print_args_type(arg1, *args, **kwargs):
print(«arg1:», arg1)
print(«*args:», args)
print(«**kwargs:», kwargs)
print_args_type(6, 1, 3, number=5)
И вот результат:
arg1: 6
*args: (1, 3)
**kwargs: {'number': 5}
Python присваивает номер 6 аргументу arg1, оставшиеся два позиционных аргумента (1 и 3) — аргументу * args, а единственный аргумент ключевого слова — ** kwargs.
Давайте также добавим фиксированный аргумент ключевого слова, который, как объяснялось ранее, должен находиться между * args и ** kwargs в сигнатуре функции:
def print_args_type(arg1, *args, kw_arg1, **kwargs):
print(«arg1:», arg1)
print(«*args:», args)
print(«kw_arg1:», kw_arg1)
print(«**kwargs:», kwargs)
print_args_type(6, 1, 3, kw_arg1=4, number=5)
И результат соответствует тому, что мы ожидаем:
arg1: 6
*args: (1, 3)
kw_arg1: 4
**kwargs: {'number': 5}
Имеет смысл?
Объяснение операторов распаковки Python
- В какой-то момент в этом руководстве я упоминал, что * и ** являются операторами распаковки.
- Но что это означает на практике?
- Начнем с печати значения кортежа без оператора * и с ним:
args = (1, 2, 3)
print(args)
print(*args)
Результат:
(1, 2, 3)
1 2 3
Итак, без оператора распаковки мы видим, что печатается весь кортеж…
… Когда мы применяем оператор распаковки *, элементы кортежа «распаковываются». Оператор печати выводит три отдельных числа.
- Это означает, что каждое из трех чисел является отдельным аргументом для функции print (), когда мы применяем оператор *.
- Оператор распаковки * применяется к итерациям (например, кортежам и спискам).
- Теперь давайте посмотрим, что происходит, когда мы применяем оператор * к словарю:
args = {'arg1': 1, 'arg2': 2}
print(args)
print(*args)
Результат:
{'arg1': 1, 'arg2': 2}
arg1 arg2
- Мы возвращаем ключи словаря только тогда, когда применяем оператор *, это не совсем то, что мы хотим.
- Для распаковки словарей следует использовать оператор **.
- Вот пример использования оператора ** для создания нового словаря, содержащего все пары ключ / значение из двух словарей:
dict1 = {'arg1': 1, 'arg2': 2}
dict2 = {'arg3': 3, 'arg4': 4}
print({**dict1, **dict2})
Результирующий словарь Python:
{'arg1': 1, 'arg2': 2, 'arg3': 3, 'arg4': 4}
Теперь вы знаете, как операторы распаковки могут помочь вам в ваших программах на Python.
Использование * args с лямбда-функцией Python
Вернемся к нашей начальной функции суммы:
def sum(x, y):
result = x + y
return result
Ниже вы можете увидеть лямбда-представление этой функции:
>>> lambda x,y : x+y
И вот как вы можете передать этой лямбде два аргумента:
>>> (lambda x,y : x+y)(1,2)
3
Теперь предположим, что мы хотим передать произвольное количество аргументов нашей лямбда-функции…
… Мы можем сделать это с помощью * args:
>>> (lambda *args: sum(args))(1,2,4,5,6)
18
>>> (lambda *args: sum(args))(3,7,6)
16
Я написал полную статью о лямбдах Python, если вам интересно узнать, как их использовать.
И последнее, что вы можете использовать * args и ** kwargs для…
Арги и варги как часть декоратора
- * args и ** kwargs также очень полезны для определения декораторов в Python.
- Вы можете использовать их в определении функции-оболочки внутри вашего декоратора с целью передачи любых аргументов декорируемой функции.
- Вот пример кода:
def verify_user(func):
def wrapper(*args, **kwargs):
if not user['logged_in']:
print(«ERROR: User {} is not logged in!».format(user['name']))
return
else:
print(«INFO: User {} is logged in».
format(user['name']))
return func(*args, **kwargs)
return wrapper
Я написал статью о декораторах Python, если вы хотите узнать, как работают декораторы и как использовать * args и ** kwargs как часть декораторов.
Заключение
Мы вместе узнали, как можно передавать переменное количество аргументов функциям Python, используя * args и ** kwargs.
Подведем итог тому, что мы рассмотрели:
- * args позволяет передавать функции переменное количество позиционных аргументов. Это кортеж, содержащий аргументы в том порядке, в котором они передаются при вызове функции.
- ** kwargs позволяет передавать функции переменное количество аргументов ключевого слова. Это словарь, поэтому мы говорим об аргументах ключевых слов. Потому что аргументы идентифицируются ключом.
В названиях этих параметров важны символы * и **. Фактические имена args и kwargs не поддерживаются интерпретатором Python и могут быть заменены чем угодно.
Основной смысл использования * args и ** kwargs — это создание гибких функций, которые можно адаптировать к нескольким вариантам использования.
Рассмотрим, например, функцию, которая вычисляет сумму двух чисел, а не сумму любых чисел. И эту концепцию можно распространить на все, что вы хотите делать со своим кодом Python.
Наконец, у нас также есть:
- посмотрел на порядок, ожидаемый интерпретатором Python для позиционных аргументов и аргументов ключевого слова.
- практиковал использование операторов распаковки * и **.
А теперь пора начать использовать * args и ** kwargs…
… Если у вас есть какие-либо вопросы, дайте мне знать в х ниже.
Первоначально опубликовано на https://codefather.tech 27 декабря 2020 г.
Больше контента на plainenglish.io
Классы, наследование, init, self, *args и **kwargs в Python
У многих Junior Python-программистов возникают проблемы с классами. Сегодня подробно разберём применение классов в Python, а также затронем тему *args и **kwargs.
Почему эти темы трудные? На начальном уровне программирования нам не требуется строить громоздкие конструкции. Обычно мы ограничиваемся скриптами без собственных классов. Или же создаем объекты классов, не задумываясь о том, что внутри.
Сегодня научимся настраивать классы под себя, добавлять в них новый функционал.
Знакомство с классами в Python
Что потребуется:
- Компьютер или любая другая ЭВМ
- Редактор кода
- Python версии 3.9 и выше
- Соединение с интернетом (впрочем можно и без него)
Начнем разбираться: для чего нужен класс? Он нужен для создания объектов с определенными функциями и данными внутри. То есть класс упрощает нам жизнь. Если у нас есть функционал взаимодействия с чем-либо и для этого нужно вызывать много функций, будет проще объединить все функции в один объект.
Например, мы взаимодействуем с базой данных. Нам приходится выполнять ряд однотипных действий:
- Подключиться к базе
- Найти нужную нам таблицу
- Сделать поиск значений по каким-то параметрам
- Вывести полученные значения.
Таких действий может быть и больше. Скоро нам надоест руками перебирать все функции для работы с БД. Тут и вступают в игру классы.
Рассмотрим на примере кода:
class Person:
def __init__(self, sex: str):
self.sex = sex
self.gender = 'heterosexual'
self.age = 0
print('I was born')
def change_gender(self, preferred_gender: str):
self.gender = preferred_gender
def describe_the_action(self, action: str):
print('I do ', action)
def __del__(self):
print('I am dead')
Сразу уточним, что функции в рамках класса будут называться методами, а переменные — полями. Обратите внимание — в Python принято записывать имя класса с большой буквы, а имена методов — с маленькой.
https://www.youtube.com/watch?v=mcAB5dBXMp4\u0026pp=ygVl0KTRg9C90LrRhtC40LggKGRlZikg0LIgUHl0aG9uOiDRgdC-0LfQtNCw0YLRjCwg0LLRi9C30LLQsNGC0YwsINCw0YDQs9GD0LzQtdC90YLRiywgKmFyZ3Mg0LggKiprd2FyZ3M%3D
Метод __init__ — это конструктор, он автоматически вызывается при создании объекта. Как только мы создали новый объект, нам выведется ‘I was born’.
Все методы принимают обязательный элемент self — он указывает, что мы обращаемся именно к этому объекту, а не к какому-то другому. Для пояснения self есть красочный пример со stackoverflow:
У нашей персоны будет его пол, гендер (стандартный при рождении) и возраст (изначально он будет равен 0). Дальше передаем аргументы.
- Теперь немного о полях: они обозначаются обращением к self через точку и название поля.
- Метод __del__ — это деконструктор, который срабатывает когда объект уничтожается (вызов del object или завершение скрипта).
- Также, мы добавляем свои собственные методы, которые будут выводить что делает человек и выполнять изменение гендера персоны.
- Создаем объект на основе класса и передаем его пол «male»:
person = Person('male')
Давайте посмотрим что выведется и попробуем изменить пол:
print('gender:', person.gender)
person.change_gender('cat')
print('gender:', person.gender)
Выводы в консоль:
- I was born
- gender: heterosexual
- gender: cat
- I am dead
Тут мы видим, что все работает корректно. Предлагаю вам самостоятельно поиграться с классом персон.
Наследование класса
Итак, мы хотим создать новый класс на базе Person. Для этого после названия класса в скобках пишем класс, от которого хотим наследоваться:
class Ivan(Person):
def __init__(self, sex: str, name: str):
super().__init__(sex)
self.name = name
Класс Ivan унаследует все методы и поля, которые есть у родителя Person.
Также, надо сделать инициализацию класса родителя. С этим нам поможет super().__init__(sex). После чего добавляем имя для Ivan, чего не было у Person. Теперь можем создать объект «man» и провести с ним некоторые операции:
man = Ivan('male', 'Ivan')
print('gender:', man.gender)
man.change_gender('cat')
print('gender:', man.gender)
Вывод в консоль:
- I was born
- gender: heterosexual
- gender: cat
- I am dead
Не забывайте, что обращаться к полям класса можно через точку:
print(man.name)
Вывод в консоль:
Ivan
Теперь вы убедились, что Ivan унаследовал все методы от Person + к ним добавилось поле «name».
Секреты *args и **kwargs
Что это за звёздочки и зачем они нужны? Начнём с *args.
Неважно какое будет название у аргумента, важен сам оператор — звёздочка. В нашем примере звёздочка сообщает Python, что если пользователь укажет больше двух аргументов, все лишние аргументы передадутся в кортеж под названием other:
def arguments(first, second, *other):
print('first:', first)
print('second:', second)
print('other:', other)
print(type(other))
arguments(1, 2, 3, 4, 5, 6, 7, 8)
Давайте посмотрим что выведется в консоль:
- first: 1
- second: 2
- other: (3, 4, 5, 6, 7, 8)
Первые аргументы, поступающие в функцию, имеют свои названия — first и second. Все остальные аргументы попали в кортеж (tuple). К other нужно обращаться по индексу, как и к обычному кортежу (подробнее о tuple тут).
Что будет, если мы не передадим в other никаких аргументов?
arguments(1, 2)
Вывод в консоль:
- first: 1
- second: 2
- other: ()
Мы просто получим пустой кортеж — никаких ошибок не будет.
Теперь разберёмся с **kwargs.
def keyword_arguments(first, second, **other):
print('first:', first)
print('second:', second)
print('other:', other)
print(type(other))
keyword_arguments(1, 2, nums=[1, 2, 3, 3], string='text', dict={'key': 'value'})
У ваc может возникнуть вопрос — что за новые аргументы nums, string и dict? Давайте запустим и посмотрим:
- first: 1
- second: 2
- other: {'nums': [1, 2, 3, 3], 'string': 'text', 'dict': {'key': 'value'}}
Удивительно, но other стал словарем с ключами в виде названий аргументов. Как мы видим, туда можно передавать любое количество аргументов с названием и значением — они будут доступны в виде словаря.
https://www.youtube.com/watch?v=mcAB5dBXMp4\u0026pp=YAHIAQE%3D
Если мы задаём всего 2 аргумента — словарь просто останется пустым:
keyword_arguments(1, 2)
Вывод в консоль:
- first: 1
- second: 2
- other: {}
Как видите, ошибок нет, мы получили пустой словарь.
Заключение
Темы классов и аргументов не просты в освоении. Надеюсь, мне удалось пролить больше света на эти удивительные возможности Python.
Чем крупнее будут становиться проекты, тем больше вам придётся иметь дело с классами. Без них просто невозможно управлять сложными объектами с большим количеством функций.
Ну а если вы не знаете заранее какие аргументы и в каком количестве вам нужны для функции, на помощь всегда придут *args и **kwargs.
Чтобы понять и закрепить материал, я рекомендую самостоятельно поиграться с классами, с *args и **kwargs. Лучше, если вы примените их на реальном проекте, чтобы как следует разобраться и усвоить детали.
Функции Python
- Функция — это блок кода с именем.
- Мы можем вызвать функцию по ее имени.
- Код внутри функции запускается только при ее вызове.
- Может принимать данные от вызывающей программы, она вызывается как параметры функции.
- Параметры функции заключены в круглые скобки и разделены запятыми. Функция может принимать любое количество аргументов.
- Функция может возвращать данные вызывающей программе. В отличие от других популярных языков программирования, определение функций Python не указывает тип возвращаемого значения.
- Мы не можем использовать зарезервированные ключевые слова в качестве имени функции. Имя должно соответствовать правилам определения идентификаторов.
Мы можем определить функцию в Python, используя ключевое слово def. Давайте посмотрим на пару примеров функции в Python.
def hello():
print('Hello World')
def add(x, y):
print(f'arguments are {x} and {y}')
return x + y
Основываясь на приведенных выше примерах, мы можем определить структуру функции как this.
def function_name(arguments):
# code statements
Как вызвать функцию в Python?
Мы можем вызвать функцию по ее имени. Если функция принимает параметры, мы должны передать их при вызове функции.
hello()
sum = add(10, 5)
print(f'sum is {sum}')
Мы вызываем определенные нами функции hello() и add(). Мы также вызываем функцию print(), которая является одной из встроенных функций в Python.
Типы
В Python есть два типа функций.
- встроенные: функции, предоставляемые языком Python, такие как print (), len (), str () и т. д.
- пользовательские: функции, определенные нами в программе Python.
Может ли функция иметь значение параметра по умолчанию?
Python допускает значения по умолчанию для параметров функции. Если вызывающий абонент не передает параметр, используется значение по умолчанию.
def hello(year=2019):
print(f'Hello World {year}')
hello(2020) # function parameter is passed
hello() # function parameter is not passed, so default value will be used
Вывод:
Hello World 2020
Hello World 2019
Можем ли мы иметь несколько операторов возврата внутри функции?
Да, функция может иметь несколько операторов возврата. Однако при достижении одного из операторов возврата выполнение функции прекращается, и значение возвращается вызывающей стороне.
def odd_even_checker(i):
if i % 2 == 0:
return 'even'
else:
return 'odd'
print(odd_even_checker(20))
print(odd_even_checker(15))
Может ли функция Python возвращать несколько значений одно за другим?
Функция Python может возвращать несколько значений одно за другим. Это реализовано с использованием ключевого слова yield. Это полезно, когда вы хотите, чтобы функция возвращала большое количество значений и обрабатывала их. Мы можем разделить возвращаемые значения на несколько частей, используя оператор yield. Этот тип функции также называется функцией генератора.
def return_odd_ints(i):
x = 1
while x
Как использовать * args и ** kwargs в Python 3
При программировании вы можете не знать обо всех возможных вариантах использования вашего кода и можете предложить больше возможностей для будущих программистов, работающих с модулем, или для пользователей, взаимодействующих с кодом. Мы можем передать переменное количество аргументов функции, используя*args и**kwargs в нашем коде.
В Python форма*args с одной звездочкой может использоваться в качестве параметра для отправки в функции списка аргументов переменной длины без ключевого слова. Стоит отметить, что звездочка (*) является здесь важным элементом, так как словоargs является общепринятой идиомой, хотя она и не поддерживается языком.
Давайте рассмотрим типичную функцию, которая использует два аргумента:
def multiply(x, y):
print (x * y)
В приведенном выше коде мы построили функцию сx иy в качестве аргументов, а затем, когда мы вызываем функцию, нам нужно использовать числа, соответствующиеx иyс. В этом случае мы передадим целое число5 дляx и целое число4 дляy:
def multiply(x, y):
print (x * y)
multiply(5, 4)
Теперь мы можем запустить приведенный выше код:
Мы получим следующий результат, показывающий, что целые числа 5 и 4 были умножены согласно функцииmultiply(x,y):
Что если позже мы решим, что мы хотели бы умножить три числа, а не только два? Если мы попытаемся добавить дополнительный номер к функции, как показано ниже, мы получим ошибку.
def multiply(x, y):
print (x * y)
multiply(5, 4, 3)
OutputTypeError: multiply() takes 2 positional arguments but 3 were given
Итак, если вы подозреваете, что вам может понадобиться использовать больше аргументов позже, вы можете вместо этого использовать*args в качестве параметра.
По сути, мы можем создать ту же функцию и код, которые мы показали в первом примере, удаливx иy как параметры функции, и вместо этого заменив их на*args:
def multiply(*args):
z = 1
for num in args:
z *= num
print(z)
multiply(4, 5)
multiply(10, 9)
multiply(2, 3, 4)
multiply(3, 5, 10, 6)
Когда мы запустим этот код, мы получим продукт для каждого из следующих вызовов функций:
Поскольку мы использовали*args для отправки нашей функции списка аргументов переменной длины, мы смогли передать столько аргументов, сколько захотим, в вызовы функции.
С помощью*args вы можете создать более гибкий код, который принимает различное количество аргументов без ключевых слов в вашей функции.
Форма**kwargs с двойной звездочкой используется для передачи аргумента переменной длиныdictionary с ключевыми словами в функцию. Опять же, две звездочки (**) являются здесь важным элементом, поскольку словоkwargs обычно используется, хотя и не поддерживается языком.
Как и*args,**kwargs может принимать любое количество аргументов, которые вы хотите ему предоставить. Однако**kwargs отличается от*args тем, что вам нужно будет назначить ключевые слова.
Во-первых, давайте просто распечатаем аргументы**kwargs, которые мы передаем функции. Мы создадим короткую функцию для этого:
def print_kwargs(**kwargs):
print(kwargs)
Далее мы вызовем функцию с некоторыми аргументами со словами, переданными в функцию:
def print_kwargs(**kwargs):
print(kwargs)
print_kwargs(kwargs_1=»Shark», kwargs_2=4.5, kwargs_3=True)
Давайте запустим программу выше и посмотрим на вывод:
Output{'kwargs_3': True, 'kwargs_2': 4.5, 'kwargs_1': 'Shark'}
В зависимости от версии Python 3, которую вы используете в данный момент, тип данных словаря может быть неупорядоченным. В Python 3.6 и выше вы будете получать пары ключ-значение по порядку, но в более ранних версиях пары будут выводиться в случайном порядке.
Важно отметить, что словарь с именемkwargs создан, и мы можем работать с ним так же, как мы можем работать с другими словарями.
Давайте создадим еще одну короткую программу, чтобы показать, как мы можем использовать**kwargs. Здесь мы создадим функцию для приветствия словаря имен. Сначала начнем со словаря из двух имен:
def print_values(**kwargs):
for key, value in kwargs.items():
print(«The value of {} is {}».format(key, value))
print_values(my_name=»Sammy», your_name=»Casey»)
Теперь мы можем запустить программу и посмотреть на результат:
OutputThe value of your_name is Casey
The value of my_name is Sammy
Опять же, поскольку словари могут быть неупорядоченными, ваш вывод может быть сначала с именемCasey, либо с именемSammy первым.
Теперь давайте передадим в функцию дополнительные аргументы, чтобы показать, что**kwargs примет любое количество аргументов, которые вы хотите включить:
def print_values(**kwargs):
for key, value in kwargs.items():
print(«The value of {} is {}».format(key, value))
print_values(
name_1=»Alex»,
name_2=»Gray»,
name_3=»Harper»,
name_4=»Phoenix»,
name_5=»Remy»,
name_6=»Val»
)
Когда мы запустим программу в этот момент, мы получим следующий вывод, который снова может быть неупорядоченным:
OutputThe value of name_2 is Gray
The value of name_6 is Val
The value of name_4 is Phoenix
The value of name_5 is Remy
The value of name_3 is Harper
The value of name_1 is Alex
Использование**kwargs дает нам гибкость в использовании аргументов ключевого слова в нашей программе. Когда мы используем**kwargs в качестве параметра, нам не нужно знать, сколько аргументов мы в конечном итоге хотели бы передать функции.
При упорядочении аргументов в вызове функции или функции аргументы должны появляться в определенном порядке:
-
Формальные позиционные аргументы
-
*args
-
Ключевые аргументы
-
**kwargs
На практике, работая с явными позиционными параметрами вместе с*args и**kwargs, ваша функция будет выглядеть так:
def example(arg_1, arg_2, *args, **kwargs):
…
И при работе с позиционными параметрами вместе с параметрами именованных ключевых слов в дополнение к*args и**kwargs ваша функция будет выглядеть так:
def example2(arg_1, arg_2, *args, kw_1=»shark», kw_2=»blobfish», **kwargs):
…
При создании функций важно помнить порядок аргументов, чтобы в коде Python не возникало синтаксической ошибки.
Мы также можем использовать*args и**kwargs для передачи аргументов в функции.
Сначала рассмотрим пример с*args.
def some_args(arg_1, arg_2, arg_3):
print(«arg_1:», arg_1)
print(«arg_2:», arg_2)
print(«arg_3:», arg_3)
args = («Sammy», «Casey», «Alex»)
some_args(*args)
В приведенной выше функции есть три параметра, определяемые какarg_1,arg_ иarg_3. Функция распечатает каждый из этих аргументов. Затем мы создаем переменную, для которой задается итерация (в данном случаеtuple), и можем передать эту переменную в функцию с синтаксисом звездочки.
Когда мы запустим программу с помощью командыpython some_args.py, мы получим следующий вывод:
Outputarg_1: Sammy
arg_2: Casey
arg_3: Alex
Мы также можем изменить указанную выше программу на итерациюlist data type с другим именем переменной. Давайте также объединим синтаксис*args сnamed parameter:
def some_args(arg_1, arg_2, arg_3):
print(«arg_1:», arg_1)
print(«arg_2:», arg_2)
print(«arg_3:», arg_3)
my_list = [2, 3]
some_args(1, *my_list)
Если мы запустим программу выше, она выдаст следующий вывод:
Outputarg_1: 1
arg_2: 2
arg_3: 3
Точно так же аргументы**kwargs с ключевыми словами могут использоваться для вызова функции. Мы создадим переменную, равную словарю, с 3 парами ключ-значение (здесь мы будем использоватьkwargs, но ее можно вызывать как угодно) и передадим ее функции с 3 аргументами:
def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
print(«kwarg_1:», kwarg_1)
print(«kwarg_2:», kwarg_2)
print(«kwarg_3:», kwarg_3)
kwargs = {«kwarg_1»: «Val», «kwarg_2»: «Harper», «kwarg_3»: «Remy»}
some_kwargs(**kwargs)
Давайте запустим приведенную выше программу с помощью командыpython some_kwargs.py:
Outputkwarg_1: Val
kwarg_2: Harper
kwarg_3: Remy
При вызове функции вы можете использовать*args и**kwargs для передачи аргументов.
Мы можем использовать специальный синтаксис*args и**kwargs в определении функции, чтобы передавать в функцию переменное количество аргументов.
Создание функций, принимающих*args и**kwargs, лучше всего использовать в ситуациях, когда вы ожидаете, что количество входов в списке аргументов останется относительно небольшим. Использование*args и**kwargs в первую очередь предназначено для обеспечения удобочитаемости и удобства, но должно выполняться с осторожностью.
Больше об именованных аргументах / Python: Функции
В этом уроке мы разберем, как получать произвольное количество именованных аргументов, как передавать их в виде коллекции и как объявлять keyword-only аргументы.
Получение именованных аргументов в виде словаря
Позиционные аргументы можно получать в виде *args, причем в произвольном количестве. Для именованных аргументов тоже существует подобная возможность. Только они получаются в виде словаря, что позволяет сохранить имена аргументов в ключах:
def g(**kwargs): return kwargs g(x=1, y=2, z=None)
# {'x': 1, 'y': 2, 'z': None}
По соглашению аргумент, который получает подобный словарь, принято называть kwargs — от словосочетания keyword arguments.
Порядок вызовов смешанных аргументов
Поэкспериментируем с разными комбинациями аргументов, которые можно передавать функциям:
def f(*args, **kwargs): return (args, kwargs) f(1, 2, 3, 4, kx='a', ky='b', kz='c')
# ((1, 2, 3, 4), {'kx': 'a', 'ky': 'b', 'kz': 'c'})
Заметим, что *args всегда указывается перед **kwargs, иначе будет ошибка:
def f(**kwargs, *args): return (kwargs, args) f(1, kx='a') # SyntaxError: invalid syntax
Также при объявлении функций можно комбинировать позиционные аргументы, значения по умолчанию, *args и **kwagrs одновременно. При использовании обычных позиционных аргументов их следует добавлять в начало перед аргументом *args:
def f(x, *args, **kwargs): return (x, args, kwargs) f(1, 2, 3, kx='a', ky='b')
# (1, (2, 3), {'kx': 'a', 'ky': 'b'})
Аргументы со значением по умолчанию следует добавлять после аргумента *args, но перед аргументом **kwagrs:
def f(*args, ky=42, **kwargs): return (args, ky, kwargs) f(1, 2, 3, 4, kz='c')
# ((1, 2, 3, 4), 42, {'kz': 'c'})
f(1, 2, 3, 4, ky='b', kz='c')
# ((1, 2, 3, 4), 'b', {'kz': 'c'})
Аргумент *args в определении функции пишется после всех обычных позиционных аргументов, но перед первым аргументом со значением по умолчанию. А **kwargs пишется в самом конце после последнего аргумента со значением по умолчанию.
Согласно этому правилу у нас идет следующий порядок расстановки аргументов:
- Обычные позиционные аргументы
- Аргумент *args
- Аргументы со значением по умолчанию
- Аргумент **kwagrs
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)
Здесь указан обычный именованный аргумент, а другой завернут в словарь.
Попробуем вызвать функцию с двумя наборами аргументов: для позиционных и для именованных:
def f(x, y, *args, kx=None, ky=42, **kwargs): return (x, y, args, kx, ky, kwargs) 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 — с именованными. При этом все именованные аргументы должны идти после всех позиционных.
Keyword-only аргументы
В 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. Поэтому опции и объявлены так, что могут быть указаны только явно.
Когда мы используем keyword-only аргументы вместе с именованными аргументами (**kwargs), возникает проблема. Именованные аргументы могут перехватить значения, которые должны были быть переданы как keyword-only аргументы. В итоге это может привести к ошибкам в работе функции.
Рассмотрим следующий пример:
def my_func(a, *, b=None, **kwargs): print(a, b, kwargs) my_func(1, b=2, c=3)
# 1 2 {'c': 3}
В данном примере аргумент a принимает значение 1, а keyword-only аргумент b — значение 2. При этом аргумент c передается в **kwargs, что может привести к ошибке, если c не должен был передаваться этой функции.
Поэтому рекомендуется использовать либо keyword-only аргументы, либо **kwargs, но не оба вместе. Это поможет избежать неожиданного поведения и ошибок в работе функции.
Порядок аргументов при вызове функций
При вызове функций у нас больше свободы, чтобы задать порядок аргументов. Одиночные именованные аргументы могут идти вперемешку с подстановками наборов позиционных. Вот пример такого вызова:
def f(x, y, *args, kx=None, ky=42, **kwargs): return (x, y, args, kx, ky, kwargs) foo = [1, 2, 3]
bar = «abc»
f(kx=42, *foo, ky=100, *bar)
# (1, 2, (3, 'a', 'b', 'c'), 42, 100, {})
Еще одна особенность заключается в том, что мы не можем одновременно указать аргумент x по имени и при этом развернуть набор параметров для функции с сигнатурой вида f(x, *args). То есть мы не сможем сделать так: f(*foo, x=42).