Как можно передать словарь в аргументы функции используя операцию распаковки
Одной из распространенных сфер, где применяются упаковка и распаковка — это параметры функций. Так, в определениях различных функций нередко можно увидеть, что они принимают такие параметры как *args и **kwargs .
Термины args и kwargs — это соглашения по программированию на Python, в реальности вместо них можно использовать любые именования. *args представляет параметры, которые передаются по позиции. А **kwargs означает параметры, которые передаются по имени. обозначает аргументы ключевого слова.
Оператор * применяется с любым итерируемым объектом (например, кортежем, списком и строками). Тогда как оператор ** можно использовать только со словарями.
Оператор * позволяет передать в функцию несколько значений, и все они будут упакованы в кортеж:
Здесь функция fun принимает кортеж значений. При вызове мы можем передать ей различное количество значений. Так, в примере выше передается четыре строки, которые образуют кортеж. Консольный вывод программы:
Благодаря такой возможности мы можем передавать в функцию переменное количество значений:
Оператор **
Оператор ** упаковывает аргументы, переданные по имени, в словарь. Имена параметров служат ключами. Например, определим функцию, которая просто будет выводить все переданные параметры
Консольный вывод программы:
Поскольку аргументы передаются в функцию в виде словаря, то внутри функции через ключи мы можем получить их значения:
Консольный вывод программы:
Распаковка аргументов
Выше было описано, как операторы * и ** применяются для упаковки аругментов в кортеж и словарь соответственно. Но эти же операторы могут использоваться для распаковки.
Оператор * и распаковка
Сначала рассмотрим ситуацию, где это может пригодиться.Пусть мы передаем в функцию кортеж:
Здесь в вызов функции sum передается кортеж. Параметр *args по сути тоже представляет кортеж, и кажется, все должно работать. Тем не менее мы столкнемся с ошибкой
То есть в данном случае кортеж numbers передается как элемент кортежа *args .
И чтобы элементы кортежа были переданы в кортеж *args как отдельные значения, необходимо выполнить их распаковку:
Здесь при передачи кортежа numbers в функцию sym применяется распаковка: *numbers
Другим случаем распаковки может быть ситуация, когда функция принимает несколько параметров, а мы передаем один кортеж или список:
В данном случае выражение *person раскладывает кортеж person на отдельные значения, которые передаются параметрам name, age и company.
Оператор ** и распаковка
Оператор ** применяется для распаковки словарей:
Здесь выражение **tom раскладывает словарь на отдельные значения, которые передаются параметрам name, age и company по названию ключей.
Сочетание параметров
Параметры *args и *kwargs могут использоваться в функции вместе с другими параметрами. Например:
Во что распаковывается словарь Python
Мы можем вызвать функцию, путем распаковки словаря d1:
Следовательно словарь d1, в качестве аргумента функции, распаковывается в конструкцию вида:
С другой стороны можно определить новый словарь как:
Но нельзя вот так:
Так во что именно распаковываются словари? Все зависит от контекста?
Вы верно предположили — всё зависит от контекста, давайте же рассмотрим это детальнее.
Примечание: Я использую Python 3.8 для испытаний
Создание словаря с помощью литералов
Рассмотрим для того, чтобы понять отличия в других ситуациях.
Это самый привычный способ, выглядит это так:
В данном примере словарь будет создан вызовом команды BUILD_CONST_KEY_MAP, которая является специализированной версией BUILD_MAP. Описание самое обыкновенное, не будем заострять на этом внимание.
Создание словаря с помощью распаковки в литералах
Этот способ отличается от первого. Главное, что нужно понять — это не имеет никакого отношения к именованным аргументам. Это специализированный способ. Python вызовет команду BUILD_MAP_UNPACK, чтобы осуществить такое создание. Описание команды гласит:
Берёт count словарей из стека, объединяет их в один словарь, и помещает результат в стек. Осуществляет распаковку словарей в словарь вида <**x, **y, **z>.
Новое в версии 3.5.
Примечание: Вольный перевод. Sorry for my English 🙂
Распаковка в конструктор класса
Python создаст кортеж из словаря, используя команду BUILD_TUPLE, а затем вызовет CALL_FUNCTION_EX, которая в свою очередь по необходимости может вызвать BUILD_MAP_UNPACK_WITH_CALL. Выдержка из описания последней команды гласит:
Это похоже на BUILD_MAP_UNPACK, но используется для f(**x, **y, **z) синтаксиса вызова.
Примечание: Вольный перевод. Sorry for my English 🙂
Распаковка словаря в именованные аргументы
Процесс абсолютно аналогичен варианту выше. В итоге можно сказать, что синтаксис ** в приведённых Вами примерах будет работать по-разному, и команды, которые он порождает, сильно отличаются.
Есть несколько разных синтаксисов для создания словаря.
Вариант 1: литерал
Инициализация словаря через литерал, элементы описываются парами ключ: значение через запятую:
Здесь и ключ, и значение – нормальные объекты Питона, поэтому ключ-имя должно быть строкой к кавычках (ну или другим hashable объектом).
Вариант 2: конструктор
Можно создать словарь через конструктор (который можно считать обычной функцией), обращаясь к классу:
В неё мы передаём содержимое словаря через именованные аргументы. При этом, часть до знака = является особой лексемой (похоже на переменные) и переводится в строку уже внутри, при создании словаря, кавычки тут неуместны.
function(**d1) – это распаковка элементов словаря в именованные (keyword) аргументы функции. Это встроенный функционал языка для передачи позиционных и именованных аргументов извне. Например, выражение func(**<'type':'Event'>) эквивалентно func(type=’Event’) – только словарь аргументов можно ещё где-то отдельно создавать, модифицировать, передавать, что бывает крайне полезно.
Поэтому, в конструктор словаря (как в обычную функцию) мы можем передать именованные аргументы из другого словаря, в итоге скопировав его содержимое:
Вариант 3: comprehension
Есть ещё comprehension синтаксис, который позволяет лаконично создавать одни итерируемые объекты из других, например:
Насчёт выражения d3 = <**d1>точно не скажу, возможно это тоже comprehension, возможно вариант литерала, а возможно отдельный синтаксис; но суть понятна, тот же dict(**d1) .
Поначалу это всё может немного путать, но не переживайте, так у всех, просто используйте те способы, которые подходят под конкретную задачу. C опытом в голове всё уляжется, а навыки выстроятся в цельную картинку 😉
Распаковка аргументов#
В Python выражения *args и **kwargs позволяют выполнять ещё одну задачу — распаковку аргументов.
До сих пор мы вызывали все функции вручную. И соответственно передавали все нужные аргументы.
В реальности, как правило, данные необходимо передавать в функцию программно. И часто данные идут в виде какого-то объекта Python.
Распаковка позиционных аргументов#
Например, при форматировании строк часто надо передать методу format несколько аргументов. И часто эти аргументы уже находятся в списке или кортеже. Чтобы их передать методу format, приходится использовать индексы таким образом:
Вместо этого, можно воспользоваться распаковкой аргументов и сделать так:
Еще один пример — функция config_interface (файл func_config_interface_unpacking.py):
Функция ожидает такие аргументы:
intf_name — имя интерфейса
Функция возвращает список строк для настройки интерфейса:
Допустим, нужно вызвать функцию и передать ей информацию, которая была получена из другого источника, к примеру, из БД.
Например, список interfaces_info, в котором находятся параметры для настройки интерфейсов:
Если пройтись по списку в цикле и передавать вложенный список как аргумент функции, возникнет ошибка:
Ошибка вполне логичная: функция ожидает три аргумента, а ей передан 1 аргумент — список.
В такой ситуации пригодится распаковка аргументов. Достаточно добавить * перед передачей списка как аргумента, и ошибки уже не будет:
Python сам „распакует“ список info и передаст в функцию элементы списка как аргументы.
Таким же образом можно распаковывать и кортеж.
Распаковка ключевых аргументов#
Аналогичным образом можно распаковывать словарь, чтобы передать его как ключевые аргументы.
Функция check_passwd (файл func_check_passwd_optional_param_2.py):
Список словарей username_passwd , в которых указано имя пользователя и пароль:
Если передать словарь функции check_passwd, возникнет ошибка:
Ошибка такая, так как функция восприняла словарь как один аргумент и считает что ей не хватает только аргумента password.
Если добавить ** перед передачей словаря функции, функция нормально отработает:
Python распаковывает словарь и передает его в функцию как ключевые аргументы. Запись check_passwd(**data) превращается в вызов вида check_passwd(username=’cisco’, password=’cisco’) .
Name already in use
pyneng-book / docs / source / book / 09_functions / func_unpacking_args.rst
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
В Python выражения *args и **kwargs позволяют выполнять ещё одну задачу — распаковку аргументов.
До сих пор мы вызывали все функции вручную. И соответственно передавали все нужные аргументы.
В реальности, как правило, данные необходимо передавать в функцию программно. И часто данные идут в виде какого-то объекта Python.
Распаковка позиционных аргументов
Например, при форматировании строк часто надо передать методу format несколько аргументов. И часто эти аргументы уже находятся в списке или кортеже. Чтобы их передать методу format, приходится использовать индексы таким образом:
Вместо этого, можно воспользоваться распаковкой аргументов и сделать так:
Еще один пример — функция config_interface (файл func_config_interface_unpacking.py):
Функция ожидает такие аргументы:
- intf_name — имя интерфейса
- ip_address — IP-адрес
- mask — маска
Функция возвращает список строк для настройки интерфейса:
Допустим, нужно вызвать функцию и передать ей информацию, которая была получена из другого источника, к примеру, из БД.
Например, список interfaces_info, в котором находятся параметры для настройки интерфейсов:
Если пройтись по списку в цикле и передавать вложенный список как аргумент функции, возникнет ошибка:
Ошибка вполне логичная: функция ожидает три аргумента, а ей передан 1 аргумент — список.
В такой ситуации пригодится распаковка аргументов. Достаточно добавить * перед передачей списка как аргумента, и ошибки уже не будет:
Python сам ‘распакует’ список info и передаст в функцию элементы списка как аргументы.
Таким же образом можно распаковывать и кортеж.
Распаковка ключевых аргументов
Аналогичным образом можно распаковывать словарь, чтобы передать его как ключевые аргументы.
Функция check_passwd (файл func_check_passwd_optional_param_2.py):
Список словарей username_passwd , в которых указано имя пользователя и пароль:
Если передать словарь функции check_passwd, возникнет ошибка:
Ошибка такая, так как функция восприняла словарь как один аргумент и считает что ей не хватает только аргумента password.
Если добавить ** перед передачей словаря функции, функция нормально отработает:
Python распаковывает словарь и передает его в функцию как ключевые аргументы. Запись check_passwd(**data) превращается в вызов вида check_passwd(username=’cisco’, password=’cisco’) .