Распаковка кортежей в Python
Признайтесь честно, вас может прельстить использование индексов при работе с кортежами, списками и другими последовательностями, в случае, когда вам нужен доступ в них к вполне конкретному элементу, а вы и понятия не имеете под каким индексом этот элемент у вас находится? Ведь, куда лучше, чтоб этот необходимый вам элемент имел бы какое-то информативно-описательное имя, через которое вы запросто смогли бы обратиться к нему. Как раз с этой целью в программировании на Python и предусмотрен трюк, называемый «распаковкой кортежей» и, известный также как «множественное присваивание» или «итерируемая распаковка».
Вместе с тем этот трюк, который на самом деле имеет довольно большой потенциал в плане упрощения понимания логики и читабельности кода, достаточно редко используется особенно начинающими программистами. Именно поэтому, далее в этой статье мы с вами на практики постараемся рассмотреть принципы использования распаковки кортежей и выяснить основные ее преимущества.
Альтернатива неинформативным и жестко закодированным индексам
Допустим, мы имеем кортеж first_tuple , состоящий из 3-х элементов:
Тогда, для того чтобы получить доступ к каждому элементу данного кортежа, первое, что напрашивается на ум – обратиться к этим элементам через их индекс:
В то же время, наш любимый Python за счет предельно простого синтаксиса запросто позволяет присваивать каждому элементу нашего кортежа необходимые информативно-описательные имена следующим образом:
Операция присваивания из вышеприведенного примера, как раз и называется распаковкой кортежа. Такое название прямо отражает сущность данной операции, в которой кортеж, состоящий из трех элементов просто, был распакован в три переменные ( x , y , и z ):
Зачастую, использование кортежей в Python коде обусловлено лишь необходимостью объединения каких-либо значений вместе. С этой точки зрения, распаковку кортежей можно воспринимать просто, как отмену предыдущего объединения вышеназванных значений. В принципе, с нашим кортежем first_tuple мы поступили абсолютно аналогичным образом, когда сначала объединили в нем три значения, а затем обратно распаковали эти значения в отдельные поименованные переменные.
Важно отметить, что если мы попытаемся распаковать наш кортеж из трех элементов не в три, а в меньшее или большее число переменных, то получим ошибку:
При распаковке какого-либо картежа всегда необходимо, чтобы его структура и перемененных, в которые он распаковывается, совпадали. Проще говоря, количество переменных, в которые распаковывается ваш картеж должно соответствовать количеству элементов самого этого картежа.
Распаковка кортежей для доступа к каждому их элементу, прежде всего, позволяет предотвратить использование жестко закодированных индексов, а также обеспечивает доступ к элементам кортежей по понятным информативно-описательным именам.
Распаковка кортежей без применения знака равенства
На практике распаковка кортежей чаще всего используется не в операторах присваивания с применением знака равенства, а в for циклах.
Но, прежде чем рассматривать распаковку кортежей в циклах, давайте подготовим себе исходные данные для этой цели. В частности, давайте вызовем метод items() для словаря furniture с тем, чтобы получить итерацию кортежей из двух элементов:
Каждый полученный нами таким образом двухэлементный кортеж на самом деле представляет собой пару ключ-значение для каждого элемента в нашем словаре. Давайте попробуем отобразить кортежи для каждого элемента этого словаря с помощью цикла for:
Из предыдущего раздела данной статьи мы уже знаем, что можем распаковать каждый элемент кортежа в соответствующую поименованную переменную. Давайте попробуем это сделать и с кортежами для элементов нашего словаря. Каждый из таких картежей, содержащий пару ключ-значение для соответствующего элемента словаря мы распакуем в переменную furniture и переменную amount соответственно:
Фактически же распаковка наших кортежей в отдельной строке внутри тела цикла с использованием оператора присваивания ( = ) было излишеством. Ведь на самом деле при каждой итерации цикла for в самой строке с определением этого цикла происходит так называемое неявное присваивание, когда в переменную, находящуюся между for и in, присваивается результат выражения, находящегося справа от in.
Таким образом, вышеприведенный пример мы можем переписать в несколько более компактной форме, производя распаковку наших кортежей прямо в строке с определением for цикла:
Следовательно, в строке с определением for цикла нам вообще не была нужна промежуточная переменная item поскольку все, что можно поместить слева от знака равенства в операторе присваивания, с таким же успехом мы можем разместить между for и in в цикле for. Вообще же, первая (определяющая) строка цикла for явится в вашей практике тем наиболее распространенным местом, куда вам потребуется распаковывать, как разнообразные кортежи, так и другие итерируемые объекты.
Выводы
В данной статье были изложены особенности распаковки последовательностей данных на примере кортежей. Однако распаковка может применяться не только к кортежам, но и к любым итерируемым объектам в Python. Именно поэтому распаковку кортежей также называют множественным присваиванием или итерируемой распаковкой.
Чаще всего вы будете сталкиваться именно с распаковками итерируемых двухэлементных или же трехэлементных кортежей при их переборе в for циклах. Хотя, на самом деле Python позволяет распаковывать кортежи и любые другие итерируемые объекты в любом месте кода, где вам может потребоваться дать описательные имена к элементам внутри данных объектов.
Кортежи в Python
Python имеет несколько последовательных типов данных, которые позволяют хранить коллекции данных организованным и эффективным способом. Основные типы последовательностей — это строки, списки, кортежи и объекты диапазонов.
В этой статье вы познакомитесь с основами кортежей Python. Мы покажем вам, как создать кортеж, получить доступ к элементам, распаковать кортеж и многое другое.
Кортежи похожи на списки с основным отличием в том, что списки изменяемы, а кортежи неизменны. Это означает, что кортежи не могут быть изменены после создания.
Кортежи могут хранить как разнородные, так и однородные данные, но обычно используются для хранения коллекций разнородных элементов.
Создание кортежей
Кортежи создаются путем помещения элементов в круглые скобки [] , разделенных запятыми. В них может быть любое количество предметов, которые могут быть разных типов. Вот пример:
Кортеж может содержать элементы со смешанными типами данных. Вы также можете объявлять вложенные кортежи, где еще одним из его элементов являются списки, кортежи или словари:
Круглые скобки без элементов между ними обозначают пустой кортеж:
Чтобы создать кортеж только с одним элементом, вы должны добавить запятую после элемента:
Кортежи также можно создавать с помощью конструктора tuple() :
Другой способ создания кортежа — использовать функцию упаковки кортежа, которая позволяет вам создать кортеж из последовательности объектов, разделенных запятыми:
Доступ к элементам кортежа
На элемент кортежа можно ссылаться по его индексу. Индексы являются целыми числами и начинаются от 0 до n-1 где n — количество элементов:
В Python индексы указываются в квадратных скобках:
Например, чтобы получить доступ к третьему элементу кортежа, вы должны tuple_name[2] :
Если вы ссылаетесь на несуществующий индекс, IndexError исключение IndexError:
Для доступа к элементам во вложенном кортеже используйте несколько индексов:
Вы также можете получить доступ к элементам кортежа, используя отрицательные индексы. Последний элемент обозначается как -1 , второй последний элемент — как -2 и так далее:
Нарезка кортежей
В Python вы можете разрезать кортеж и другие последовательные типы данных, используя следующую форму:
- start — это индекс, с которого начинается извлечение. Когда используется отрицательный индекс, он указывает смещение от конца кортежа. Если этот аргумент опущен, нарезка начинается с индекса 0.
- stop — индекс, до которого следует завершить извлечение; результат не включает элемент «стоп». Когда используется отрицательный индекс, он указывает смещение от конца кортежа. Если этот аргумент опущен или превышает длину кортежа, нарезка выполняется до конца кортежа.
- step — необязательный аргумент, указывающий шаг нарезки. Если не указано, по умолчанию используется 1. Если используется отрицательное значение, срез принимает элементы в обратном порядке.
Результатом нарезки кортежа является новый кортеж, содержащий извлеченные элементы.
Следующие формы допустимы в Python:
Ниже приведен пример того, как нарезать кортеж, начиная с элемента с индексом 1 до элемента с индексом 4, но не включая его:
Распаковка кортежей
Распаковка последовательности в функции Python, которая позволяет вам назначать объекты последовательности переменным. Вот пример:
Значения элементов кортежа в соответствии с их положением присваиваются переменным слева:
При распаковке кортежей количество переменных в левом кортеже должно быть таким же, как количество элементов кортежа. В противном случае вы получите исключение ValueError
Распаковка удобна, когда метод или функция возвращает последовательность объектов:
Изменение кортежа
Поскольку кортежи являются неизменяемыми структурами данных, мы не можем обновлять их напрямую. Вы не можете добавлять, изменять, удалять элементы в кортежах.
Если вы попытаетесь изменить элемент кортежа, вы получите исключение TypeError
Элементы изменяемого кортежа могут быть изменены. Например, если кортеж имеет список в качестве одного из его элементов, вы можете обновить элементы списка:
Длина кортежа
Встроенная len() возвращает общее количество элементов данного объекта.
Чтобы найти длину кортежа, передайте его в качестве аргумента функции len() :
Итерация по кортежу
Чтобы перебрать все элементы в кортеже, вы можете использовать цикл for
Если вам нужны индексы, в вашем распоряжении есть несколько методов. Наиболее распространенные способы — комбинировать функции range() и len() или использовать встроенную функцию enumerate()
В приведенном ниже примере показано, как получить индекс и значение каждого элемента в кортеже:
Вместо использования range(len(. )) вы можете использовать enumerate() для перебора кортежа более питоническим способом:
Проверить, существует ли элемент
Чтобы проверить, существует ли элемент в кортеже, вы можете использовать операторы in а not in
Результатом будет True или False :
Вот еще один пример с использованием оператора if :
Кортежные методы
Объект кортежа принимает следующие методы:
- count(x) — возвращает количество раз, когда «x» встречается в кортеже.
- index(x) — возвращает позицию первого вхождения элемента со значением ‘x’.
Ниже приведен простой пример, показывающий, как использовать эти методы:
Вывод
В Python кортежи — это неизменяемые последовательности объектов, которые нельзя изменить после создания.
Если у вас есть какие-либо вопросы или отзывы, не стесняйтесь оставлять комментарии.
pelid / Tuple и распаковка в Python.md
Представьте себя функцией. Да, это не просто, и все же.
Вас зовут get_contacts и вы умеете находить контакты человека по email-адресу: его адрес, имя и номер телефона. Вы не прочь поделиться информацией с другими, но Python вам это запрещает. Нельзя возвращать так много данных разом, результат у функции может быть только один. Вы можете выбрать что-то одно, самое важное: адрес, имя, или телефон человека. Или можете проявить изобретательность и упаковать все свои данные в одну переменную.
Для функции get_contacts(email) доступны несколько способов упаковки данных в более сложную структуру: словарь dict() , кортеж tuple() и еще несколько других. Рассмотрим подробнее последний вариант — tuple .
Предположим, вы вызвали функцию get_contacts(‘dmitry@example.com’) , она нашла такого человека и хочет вернуть его контакты:
Функция создает кортеж — tuple() —, чтобы поместить в него все контакты:
Круглые скобки и запятые указывают Python, что из перечисленных переменных full_name , city и phone_number нужно создать новую структуру данных:
Последний вызов print() выведет на экран такое:
Теперь функция get_contacts сможет вернуть любое количество данных. Все они будут упакованы в один кортеж — tuple .
Что делать если получили tuple
Разберем как работать с tuple на примере стандартной функции divmod . Она возвращает результат деления с остатком:
Функция divmod возвращает кортеж из двух чисел — результата целочисленного деления и остаток от деления.
Мы можем сохранить результат в переменную:
Добраться до каждого из двух чисел можно по индексу:
Индексы в коде [0] и [1] сильно осложняют чтение, поэтому полезно давать говорящие названия:
Такой паттерн рекомендуется к использованию и встречается настолько часто, что в Python ввели упрощенную короткую запись:
Запись похожа на создание кортежа — те же скобки и запятые, но работает это в обратную сторону. Называется «распаковкой».
Если в кортеже больше одного элемента, то скобки указывать необязательно. Python любит чистый код:
Последняя форма записи распаковки наиболее распространена и рекомендуется к использованию.
Скобки не обязательно указывать и при создании кортежа, но этот прием может усложнить чтение кода, будьте с ним осторожны:
В коде внутри функций часто встречается еще более короткая запись:
Вызов return сообщает интерпретатору Python, что функция завершает свою работу и хочет вернуть результат. А все перечисленное справо от return будет упаковано в кортеж — tuple .
Rukovodstvo
статьи и идеи для разработчиков программного обеспечения и веб-разработчиков.
Распаковка в Python: помимо параллельного присвоения
Введение Распаковка в Python относится к операции, которая состоит из присвоения итерации значений кортежу [/ lists-vs-tuples-in-python /] (или списку) переменных в одном операторе присваивания. В качестве дополнения можно использовать термин упаковка, когда мы собираем несколько значений в одной переменной с помощью итеративного оператора распаковки *. Исторически сложилось так, что разработчики Python обычно называли этот вид операции распаковкой кортежа. Однако, поскольку эта функция Python оказалась
Время чтения: 20 мин.
Вступление
Распаковка в Python относится к операции, которая состоит из присвоения итерации значений кортежу (или list ) переменных в одном операторе присваивания. В качестве дополнения можно использовать термин упаковка , когда мы собираем несколько значений в одной переменной с помощью итеративного оператора распаковки * .
Исторически сложилось так, что разработчики Python обычно называли этот вид операции распаковкой кортежа . Однако, поскольку эта функция Python оказалась весьма полезной и популярной, она была распространена на все виды итераций. В наши дни более современным и точным термином будет итеративная распаковка .
В этом руководстве мы узнаем, что такое итеративная распаковка и как мы можем воспользоваться этой функцией Python, чтобы сделать наш код более читабельным, поддерживаемым и питоническим.
Кроме того, мы также рассмотрим несколько практических примеров того, как использовать функцию повторяющейся распаковки в контексте операций присваивания, for циклов, определений функций и вызовов функций.
Упаковка и распаковка в Python
Python позволяет tuple (или list ) переменных слева от операции присваивания. Каждая переменная в tuple может получать одно значение (или несколько, если мы используем * ) из итерации в правой части присваивания.
По историческим причинам разработчики Python называли это распаковкой кортежей . Однако, поскольку эта функция была обобщена для всех видов итераций, более точным термином будет итеративная распаковка, и именно так мы назовем ее в этом руководстве.
Операции распаковки были довольно популярны среди разработчиков Python, потому что они могут сделать наш код более читаемым и элегантным. Давайте подробнее рассмотрим распаковку в Python и посмотрим, как эта функция может улучшить наш код.
Распаковка кортежей
В Python мы можем поместить tuple переменных слева от оператора присваивания ( = ) и tuple значений справа. Значения справа будут автоматически присвоены переменным слева в соответствии с их положением в tuple . В Python это широко известно как распаковка кортежей. Посмотрите следующий пример:
Когда мы помещаем кортежи по обе стороны от оператора присваивания, происходит операция распаковки кортежа. Значения справа присваиваются переменным слева в соответствии с их относительным положением в каждом tuple . Как вы можете видеть в приведенном выше примере, a будет 1 , b будет 2 , а c будет 3 .
Чтобы создать tuple , нам не нужно использовать пару круглых скобок () качестве разделителей. Это также работает при распаковке кортежей, поэтому следующие синтаксисы эквивалентны:
Поскольку все эти варианты являются допустимым синтаксисом Python, мы можем использовать любой из них, в зависимости от ситуации. Возможно, последний синтаксис чаще используется, когда дело доходит до распаковки в Python.
Когда мы распаковываем значения в переменные с помощью распаковки кортежа, количество переменных в левом tuple должно точно соответствовать количеству значений в правом tuple . В противном случае мы получим ValueError .
Например, в следующем коде мы используем две переменные слева и три значения справа. Это вызовет ValueError сообщающую нам, что слишком много значений для распаковки:
Примечание: Единственное исключение из этого — когда мы используем * для упаковки нескольких значений в одну переменную, как мы увидим позже.
С другой стороны, если мы используем больше переменных, чем значений, мы получим ValueError но на этот раз сообщение говорит, что недостаточно значений для распаковки:
Если мы используем другое количество переменных и значений в операции распаковки кортежа, мы получим ValueError . Это потому, что Python должен однозначно знать, какое значение входит в какую переменную, чтобы он мог выполнять присвоение соответственно.
Распаковка итераций
Функция распаковки кортежей стала настолько популярной среди разработчиков Python, что синтаксис был расширен для работы с любым итеративным объектом. Единственное требование состоит в том, чтобы итерация давала ровно один элемент для каждой переменной в принимающем tuple (или list ).
Ознакомьтесь со следующими примерами того, как итеративная распаковка работает в Python:
Когда дело доходит до распаковки в Python, мы можем использовать любую итерацию справа от оператора присваивания. Левая часть может быть заполнена tuple или list переменных. Посмотрите следующий пример, в котором мы используем tuple в правой части оператора присваивания:
Это работает точно так же, если мы используем итератор range()
Несмотря на то, что это допустимый синтаксис Python, он обычно не используется в реальном коде и может немного сбивать с толку начинающих разработчиков Python.
Наконец, мы также можем использовать set объекты в операциях распаковки. Однако, поскольку наборы представляют собой неупорядоченную коллекцию, порядок назначений может быть в некотором роде непоследовательным и может привести к незаметным ошибкам. Посмотрите следующий пример:
Если мы используем наборы в операциях распаковки, то окончательный порядок назначений может сильно отличаться от того, что мы хотим и ожидаем. Поэтому лучше избегать использования наборов в операциях распаковки, если порядок присваивания не важен для нашего кода.
Упаковка с оператором *
В этом контексте оператор * известен как оператор распаковки кортежа (или итерируемого) . Он расширяет функциональность распаковки, чтобы мы могли собирать или упаковывать несколько значений в одну переменную. В следующем примере мы упаковываем tuple значений в одну переменную с помощью оператора *
Чтобы этот код работал, левая часть присваивания должна быть tuple (или list ). Вот почему мы используем конечную запятую. Этот tuple может содержать столько переменных, сколько нам нужно. Однако он может содержать только одно помеченное звездочкой выражение .
Мы можем сформировать начальное выражение, используя оператор распаковки * вместе с действительным идентификатором Python, как *a в приведенном выше коде. Остальные переменные в левом tuple называются обязательными переменными, потому что они должны быть заполнены конкретными значениями, иначе мы получим ошибку. Вот как это работает на практике.
Упаковка конечных значений в b :
Упаковка начальных значений в : a
Упаковка одного значения в a потому что b и c являются обязательными:
Не упаковывать значения в a ( a умолчанию — [] ), потому что b , c и d являются обязательными:
Отсутствует значение обязательной переменной ( e ), поэтому возникает ошибка:
Упаковка значений в переменную с помощью * может быть удобной, когда нам нужно собрать элементы генератора в одну переменную без использования функции list() . В следующих примерах мы используем * для упаковки элементов выражения генератора и объекта диапазона в отдельную переменную:
В этих примерах * оператор упаковывает элементы в gen , и ran во g и r соответственно. Благодаря его синтаксису мы избегаем необходимости вызывать list() для создания list значений из range , выражения генератора или функции генератора.
Обратите внимание, что мы не можем использовать оператор распаковки * , чтобы упаковать несколько значений в одну переменную, не добавляя запятую в конце переменной в левой части присваивания. Итак, следующий код работать не будет:
Если мы попытаемся использовать * для упаковки нескольких значений в одну переменную, тогда нам потребуется использовать синтаксис tuple Например, чтобы приведенный выше пример работал, нам просто нужно добавить запятую после переменной r , как в *r, = range(10) .
Использование упаковки и распаковки на практике
Операции по упаковке и распаковке могут оказаться весьма полезными на практике. Они могут сделать ваш код понятным, читаемым и питоническим. Давайте рассмотрим некоторые распространенные варианты использования упаковки и распаковки в Python.
Назначение параллельно
Один из наиболее распространенных вариантов использования распаковки в Python — это то, что мы можем назвать параллельным присваиванием . Параллельное присваивание позволяет вам присваивать значения в итерации tuple (или list ) переменных в одном элегантном операторе.
Например, предположим, что у нас есть база данных о сотрудниках в нашей компании, и нам нужно назначить каждый элемент в списке описательной переменной. Если мы проигнорируем, как итеративная распаковка работает в Python, мы можем написать такой код:
Несмотря на то, что этот код работает, обработка индекса может быть неуклюжей, сложной для ввода и запутанной. Более чистое, более читаемое и питоническое решение можно закодировать следующим образом:
Используя распаковку в Python, мы можем решить проблему из предыдущего примера с помощью одного простого и элегантного оператора. Это крошечное изменение сделало бы наш код более легким для чтения и понимания для начинающих разработчиков.
Обмен значениями между переменными
Еще одно элегантное применение распаковки в Python — это обмен значениями между переменными без использования временной или вспомогательной переменной. Например, предположим, что нам нужно поменять местами значения двух переменных a и b . Для этого мы можем придерживаться традиционного решения и использовать временную переменную для хранения значения, которое будет заменено следующим образом:
Эта процедура состоит из трех шагов и новой временной переменной. Если мы используем распаковку в Python, то мы можем добиться того же результата за один краткий шаг:
В заявлении a, b = b, a мы переназначаем a на b и b на a в одной строке кода. Это намного удобнее и понятнее. Также обратите внимание, что при использовании этого метода нет необходимости в новой временной переменной.
Сбор нескольких значений с помощью *
Когда мы работаем с некоторыми алгоритмами, могут возникать ситуации, в которых нам нужно разделить значения итерации или последовательности на порции значений для дальнейшей обработки. В следующем примере показано, как для этого использовать list и операции нарезки :
Несмотря на то, что этот код работает так, как мы ожидаем, работа с индексами и срезами может быть немного раздражающей, сложной для чтения и запутанной для новичков. У него также есть недостаток, заключающийся в том, что код становится жестким и трудным в поддержке. В этой ситуации итеративный оператор распаковки * и его способность упаковывать несколько значений в одну переменную могут быть отличным инструментом. Посмотрите этот рефакторинг приведенного выше кода:
Здесь волшебство first, *body, last = seq Оператор итеративной распаковки * собирает элементы в середине seq в body . Это делает наш код более читабельным, поддерживаемым и гибким. Вы можете подумать, а почему более гибкие? Что ж, предположим, что seq меняет свою длину в дороге, и вам все еще нужно собрать средние элементы в body . В этом случае, поскольку мы используем распаковку в Python, для работы нашего кода никаких изменений не требуется. Посмотрите этот пример:
Если бы мы использовали нарезку последовательности вместо итеративной распаковки в Python, тогда нам нужно было бы обновить наши индексы и фрагменты, чтобы правильно улавливать новые значения.
Использование * для упаковки нескольких значений в одну переменную может применяться в различных конфигурациях при условии, что Python может однозначно определять, какой элемент (или элементы) назначить каждой переменной. Взгляните на следующие примеры:
Мы можем переместить оператор * tuple (или list ) переменных, чтобы собрать значения в соответствии с нашими потребностями. Единственное условие — Python может определять, какой переменной присвоить каждое значение.
Важно отметить, что мы не можем использовать более одного начального выражения в присваивании. Если мы это сделаем, то получим SyntaxError следующим образом:
Если мы используем два или более * в выражении присваивания, то мы получим SyntaxError сообщающее нам, что найдено выражение, помеченное двумя звездами. Это так, потому что Python не может однозначно определить, какое значение (или значения) мы хотим присвоить каждой переменной.
Удаление ненужных значений с помощью *
Другой распространенный вариант использования * — использовать его с фиктивным именем переменной для удаления некоторых бесполезных или ненужных значений. Посмотрите следующий пример:
В качестве более наглядного примера этого варианта использования предположим, что мы разрабатываем сценарий, который должен определять используемую версию Python. Для этого мы можем использовать атрибут sys.version_info . Этот атрибут возвращает кортеж , содержащий пять компонентов номера версии: major , minor , micro , releaselevel и serial . Но нам нужны только major , minor и micro чтобы наш скрипт работал, так что мы можем отказаться от остальных. Вот пример:
Теперь у нас есть три новые переменные с нужной нам информацией. Остальная информация хранится в фиктивной переменной _ , которую наша программа может игнорировать. Это может дать понять начинающим разработчикам, что мы не хотим (или должны) использовать информацию, хранящуюся в _ потому что этот символ не имеет очевидного значения.
Примечание. По умолчанию символ подчеркивания _ используется интерпретатором Python для хранения результирующего значения операторов, которые мы запускаем в интерактивном сеансе. Таким образом, в этом контексте использование этого символа для идентификации фиктивных переменных может быть неоднозначным.
Возврат кортежей в функциях
Функции Python могут возвращать несколько значений, разделенных запятыми. Поскольку мы можем определять tuple без использования круглых скобок, этот вид операции можно интерпретировать как возврат tuple значений. Если мы кодируем функцию, которая возвращает несколько значений, мы можем выполнять итерационные операции упаковки и распаковки с возвращенными значениями.
Посмотрите следующий пример, в котором мы определяем функцию для вычисления квадрата и куба заданного числа:
Если мы определим функцию, которая возвращает значения, разделенные запятыми, то мы можем выполнить любую операцию упаковки или распаковки этих значений.
Слияние итераций с оператором *
Еще один интересный вариант использования оператора распаковки * — это возможность объединить несколько итераций в финальную последовательность. Эта функция работает для списков, кортежей и наборов. Взгляните на следующие примеры:
Мы можем использовать итеративный оператор распаковки * при определении последовательностей, чтобы распаковать элементы подпоследовательности (или итерируемой) в конечную последовательность. Это позволит нам создавать последовательности на лету из других существующих последовательностей без вызова таких методов, как append() , insert() и т. Д.
Последние два примера показывают, что это также более удобный и эффективный способ объединения итераций. Вместо записи list(my_set) + my_list + list(my_tuple) + list(range(1, 4)) + list(my_str) мы просто пишем [*my_set, *my_list, *my_tuple, *range(1, 4), *my_str] .
Распаковка словарей с помощью ** Оператора
В контексте распаковки в Python оператор ** называется оператором распаковки словаря . Использование этого оператора было расширено в PEP 448 . Теперь мы можем использовать его в вызовах функций, в интерпретациях и выражениях генератора, а также в отображениях .
Базовый вариант использования оператора распаковки словарей — объединить несколько словарей в один окончательный словарь с одним выражением. Посмотрим, как это работает:
Если мы используем оператор распаковки словаря внутри отображения словаря, мы можем распаковать словари и объединить их для создания окончательного словаря, который включает пары ключ-значение исходных словарей, как мы это сделали в приведенном выше коде.
Важно отметить, что если словари, которые мы пытаемся объединить, имеют повторяющиеся или общие ключи, то значения самого правого словаря будут иметь приоритет над значениями самого левого словаря. Вот пример:
Поскольку a присутствует в обоих словарях, преобладающее значение исходит от vowels , то есть самого правого словаря. Это происходит потому, что Python начинает добавлять пары ключ-значение слева направо. Если в процессе Python находит ключи, которые уже вышли, интерпретатор обновляет эти ключи новым значением. Вот почему в приведенном выше примере a
Распаковка в For-Loops
Мы также можем использовать итеративную распаковку в контексте циклов for Когда мы запускаем for , цикл назначает один элемент своей итерации целевой переменной на каждой итерации. Если присваиваемый элемент является итерируемым, то мы можем использовать tuple целевых переменных. Цикл распакует итерацию под рукой в tuple целевых переменных.
В качестве примера предположим, что у нас есть файл, содержащий следующие данные о продажах компании:
Продукт Цена Проданные единицы
Карандаш 0,25 1500 Ноутбук 1,30 550 Ластик 0,75 1000 . . .
Из этой таблицы мы можем построить list двухэлементных кортежей. Каждый tuple будет содержать название продукта, цену и проданные единицы. Имея эту информацию, мы хотим рассчитать доход от каждого продукта. Для этого мы можем использовать такой цикл for
Этот код работает так, как ожидалось. Однако мы используем индексы, чтобы получить доступ к отдельным элементам каждого tuple . Это может быть трудно прочитать и понять начинающим разработчикам.
Давайте посмотрим на альтернативную реализацию с использованием распаковки в Python:
Теперь мы используем итеративную распаковку в нашем цикле for Это делает наш код более читабельным и поддерживаемым, поскольку мы используем описательные имена для идентификации элементов каждого tuple . Это крошечное изменение позволит новичку-разработчику быстро понять логику кода.
Также можно использовать оператор * for чтобы упаковать несколько элементов в одну целевую переменную:
В этом for цикла, мы ловим первый элемент каждой последовательности в first . Затем оператор * list значений в своей целевой переменной rest .
Наконец, структура целевых переменных должна соответствовать структуре итерируемого. В противном случае мы получим ошибку. Взгляните на следующий пример:
В первом цикле структура целевых переменных (a, b), c согласуется со структурой элементов в итерации ((1, 2), 2) . В этом случае цикл работает должным образом. Напротив, второй цикл использует структуру целевых переменных, которые не согласуются со структурой элементов в итерируемом объекте, поэтому цикл завершается ошибкой и вызывает ValueError .
Упаковка и распаковка в функциях
Мы также можем использовать функции упаковки и распаковки Python при определении и вызове функций. Это довольно полезный и популярный вариант упаковки и распаковки в Python.
В этом разделе мы рассмотрим основы использования упаковки и распаковки в функциях Python либо в определении функции, либо в ее вызове.
Примечание. Для получения более глубокого и подробного материала по этим темам ознакомьтесь с аргументами переменной длины в Python с *args и **kwargs .
Определение функций с помощью * и **
Мы можем использовать * и ** в сигнатуре функций Python. Это позволит нам вызывать функцию с переменным количеством позиционных аргументов ( * ) или с переменным количеством аргументов ключевого слова, или с обоими. Рассмотрим следующую функцию:
Для указанной выше функции требуется по крайней мере один аргумент с именем required . Он также может принимать переменное количество позиционных и ключевых аргументов. В этом случае * собирает или упаковывает дополнительные позиционные аргументы в кортеж с именем args а ** собирает или упаковывает дополнительные аргументы ключевого слова в словарь с именем kwargs . Оба, args и kwargs , являются необязательными и автоматически устанавливаются по умолчанию в () и <> соответственно.
Несмотря на то, что имена args и kwargs широко используются сообществом Python, они не являются обязательными для работы этих методов. Синтаксис просто требует * или ** за которым следует действительный идентификатор. Итак, если вы можете дать этим аргументам осмысленные имена, то сделайте это. Это, безусловно, улучшит читаемость вашего кода.
Вызов функций с помощью * и **
При вызове функций мы также можем извлечь выгоду из использования * и ** для распаковки наборов аргументов в отдельные позиционные или ключевые аргументы соответственно. Это противоположно использованию * и ** в сигнатуре функции. В подписи операторы означают сбор или упаковку переменного количества аргументов в один идентификатор. В вызове они означают распаковку итерации на несколько аргументов.
Вот простой пример того, как это работает:
Здесь * распаковывает последовательности вроде ["Welcome", "to"] в позиционные аргументы. Аналогичным образом ** распаковывает словари в аргументы, имена которых соответствуют ключам распакованного словаря.
Мы также можем комбинировать этот метод и метод, описанный в предыдущем разделе, для написания довольно гибких функций. Вот пример:
Использование * и ** при определении и вызове функций Python предоставит им дополнительные возможности и сделает их более гибкими и мощными.
Заключение
Итеративная распаковка оказалась довольно полезной и популярной функцией в Python. Эта функция позволяет нам распаковать итерацию на несколько переменных. С другой стороны, упаковка состоит из захвата нескольких значений в одну переменную с помощью оператора распаковки * .
В этом руководстве мы узнали, как использовать итеративную распаковку в Python для написания более читаемого, поддерживаемого и питонического кода.
Обладая этими знаниями, теперь мы можем использовать итеративную распаковку в Python для решения распространенных проблем, таких как параллельное присваивание и обмен значениями между переменными. Мы также можем использовать эту функцию , Python и в других структурах , как for петель, вызовов функций и определений функций.