Как распаковать список в python
Перейти к содержимому

Как распаковать список в python

  • автор:

Простая и расширенная распаковка списков и кортежей

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

a, b, c = iterable 

Эта инструкция, в которой требуется, чтобы объект iterable был итерируемым и содержал ровно три элемента, связывает а с первым элементом, b — со вторым и c — с третьим. Операции такого рода носят название присваивание с распаковкой. Выражение в правой части должно быть итерируемым и содержать ровно столько элементов, сколько указано ссылок в левой части, в противном случае Python бросает исключение. Каждая из ссылок, указанных в левой части, связывается с соответствующим элементом из правой части.

Присваивание с распаковкой можно использовать для обмена значениями переменных:

a, b = b, a 

Данная инструкция присваивания повторно связывает имя a со значением, с которым было связано имя b , и наоборот.

Если имеется несколько целевых ссылок, то разрешается помечать одну из них символом * — звездочка. Целевая ссылка, которой предшествует символ * , связывается со списком всех элементов, оставшихся не связанными с другими целевыми ссылками.

first, *middle, last = iterable # что эквивалентно записи без '*' звездочки (расширенной распаковки) first, middle, last = iterable[0], iterable[1:-1], iterable[-1] 

В обеих инструкциях присваивания требуется, чтобы объект iterable имел по крайней мере два элемента. Эту форму операции присваивания называют расширенной распаковкой.

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

Например, используя множественное присваивание дважды, можно сделать следующее:

>>> colour_info = ("AliceBlue", (240, 248, 255)) >>> name, rgb_values = colour_info >>> name # 'AliceBlue' >>> r, g, b = rgb_values >>> g # 248 

Но если необходимо получить отдельные значения RGB, то можно сразу использовать глубокую распаковку:

>>> colour_info = ("AliceBlue", (240, 248, 255)) >>> name, (r, g, b) = colour_info >>> name # 'AliceBlue' >>> g # 248 

Обратите внимание, что переменные r , g и b группируются с помощью скобок () , для создания кортежа, имитирующего форму переменной colour_info . Если просто написать name, r, g, b = colour_info , то Python будет пытаться выполнить четыре присваивания, и ожидал бы от переменной colour_info последовательность из 4-х элементов:

>>> colour_info = ("AliceBlue", (240, 248, 255)) >>> name, r, g, b = colour_info # Traceback (most recent call last): # File "", line 1, in # ValueError: not enough values to unpack (expected 4, got 2) 

Глубокая/расширенная распаковка также может использоваться в неявном назначении циклов for/in , она не обязательно должна быть в явном виде со знаком равенства!

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

def greyscale(colour_info): # для вычисления оттенка, функция использует срезы return 0.2126*colour_info[1][0] + 0.7152*colour_info[1][1] + 0.0722*colour_info[1][2] colours = [ # . Ошибка в задании цвета `AliceBlue`. ("AliceBlue", (240, 248, 255, 127, 255, 212)), ("DarkCyan", (0, 139, 139)), ] # . функция отработала без ошибок. print(greyscale(colours[0])) # 246.8046 

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

def greyscale(colour_info): # код вычисления оттенка более лаконичный и короткий name, (r, g, b) = colour_info return 0.2126*r + 0.7152*g + 0.0722*b colours = [ # . Ошибка в задании цвета `AliceBlue`. ("AliceBlue", (240, 248, 255, 127, 255, 212)), ("DarkCyan", (0, 139, 139)), ] # вот и ошибка print(greyscale(colours[0])) # ValueError: too many values to unpack (expected 3) 

Примеры распаковки/упаковки последовательностей.

>>> a, b, c = 0, 5, 10 >>> a, b, c # 0 5 10 >>> a, b, c = ['one', 'two', 'three'] >>> a, b, c # one two three >>> a, b = b, a >>> a, b # two one >>> a, *b = ['one', 'two', 'three'] >>> a, b # one ['two', 'three'] 

Пример реализации функции, похожей на функцию reduce из модуля functools при помощи распаковки последовательности.

def reduce(function, list_): """Уменьшение списка с помощью функции.""" if not list_: raise TypeError("Cannot reduce empty list.") value, *list_ = list_ while list_: val, *list_ = list_ value = function(value, val) return value 

Распаковка итерируемых объектов в переменные с помощью расширенной распаковки:

>>> fruits = ['lemon', 'orange', 'banana', 'tomato'] >>> first, second, *orher = fruits >>> orher # ['banana', 'tomato'] >>> first, *orher = fruits >>> orher # ['orange', 'banana', 'tomato'] >>> first, *middle, last = fruits >>> middle # ['orange', 'banana'] 

Распаковка итерируемых объектов может быть вложенной:

>>> fruits = ['lemon', 'orange', 'banana', 'tomato'] >>> ((first_letter, *other_letter), *other) = fruits >>> first_letter, other_letter # l ['e', 'm', 'o', 'n'] >>> other # ['orange', 'banana', 'tomato'] 

Теперь представим, что есть список с несколькими цветами и нужно вычислить оттенки серого. Для этого можно использовать глубокую распаковку в цикле for/in (а также используя генератор списка):

colours = [ ("AliceBlue", (240, 248, 255)), ("Aquamarine", (127, 255, 212)), ("DarkCyan", (0, 139, 139)), ] greyscales = [ round(0.2126*r + 0.7152*g + 0.0722*b, 2) for name, (r, g, b) in colours ] print(greyscales) # [246.8, 224.68, 109.45] 

Упаковка итерируемых объектов (сложение) в новый список:

>>> fruits = ['lemon', 'orange', 'banana', 'tomato'] >>> x = [*fruits, *reversed(fruits)] >>> x # ['lemon', 'orange', 'banana', 'tomato', 'tomato', 'banana', 'orange', 'lemon'] >>>x = [*fruits[2:], *fruits[:2]] >>> x # ['banana', 'tomato', 'lemon', 'orange'] # Здесь создадим кортеж >>> x = (*fruits[1:], fruits[0]) >>> x # ('orange', 'banana', 'tomato', 'lemon') 
  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Обычный способ присваивания значений
  • Групповое присваивания значений
  • Распаковка/упаковка последовательностей
  • Присваивание значений на месте
  • Присваивание значений в выражении walrus

Распаковка аргументов для передачи в функцию Python

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

Например, встроенная range() функция ожидает отдельные аргументы start и stop . Если они не доступны отдельно, то можно распаковать аргументы из списка list или кортежа tuple , вызвав функцию с переменной этого списка/кортежа, а впереди нее поставить символ одной звездочки * :

# обычный вызов с отдельными аргументами >>> list(range(3, 6)) # [3, 4, 5] >>> args = [3, 6] # вызов с аргументами, распакованными из списка >>> list(range(*args)) # [3, 4, 5] 

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

>>> def parrot(voltage, state='a stiff', action='voom'): . print("-- This parrot wouldn't", action, end=' ') . print("if you put", voltage, "volts through it.", end=' ') . print("E's", state, "!") . >>> d = "voltage": "four million", "state": "bleedin' demised", "action": "VOOM"> >>> parrot(**d) # -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demis 
  • символом * — распаковывают последовательности в качестве элементов которых одно значение (списки, кортежи, множества)
  • два символа ** — распаковывают последовательности в качестве элементов которых имеется ключ и значение (словари)

Примеры распаковки списков и словарей с аргументами для функции:

date_inf = 'year': "2020", 'month': "12", 'day': "06"> f_name = " - - .txt".format(**date_inf) print(f_name) # 2020-12-06.txt fruits = ['lemon', 'orange', 'banana', 'tomato'] print(*fruits) # lemon orange banana tomato 

Распаковку аргументов можно использовать несколько раз в функции:

date_info = 'year': "2020", 'month': "01", 'day': "01"> track_info = 'artist': "Beethoven", 'title': 'Symphony No 5'> filename = " - - - - .txt".format(**date_info, **track_info) print(filename) # 2020-01-01-Beethoven-Symphony No 5.txt 
fruits = ['lemon', 'orange', 'banana', 'tomato'] first, second, *orher = fruits print(orher) # ['banana', 'tomato'] first, *orher = fruits print(orher) # ['orange', 'banana', 'tomato'] first, *middle, last = fruits print(middle) # ['orange', 'banana'] 
  • КРАТКИЙ ОБЗОР МАТЕРИАЛА.
  • Функции это объекты
  • Функции могут иметь атрибуты
  • Функции могут храниться в структурах данных
  • Функции могут быть вложенными
  • Передача функции в качестве аргумента другой функции
  • Область видимости переменных функции
  • Операторы global и nonlocal
  • Параметры (аргументы) функции
  • Ключевые аргументы в определении функции Python
  • Значение аргумента по умолчанию в функциях Python
  • Варианты передачи аргументов в функцию Python
  • Переменные аргументов *args и **kwargs в функции Python
  • Распаковка аргументов для передачи в функцию Python
  • Как оцениваются аргументы при вызове функции?
  • Строгие правила передачи аргументов в функцию Python
  • Инструкция return
  • Анонимные функции (lambda-выражения)
  • Строки документации в функциях Python
  • Рекурсия
  • Замыкания в функциях Python
  • Перегрузка функций

Как распаковать список в python

Распаковка ( unpacking , также называемая Деструктуризация ) представляет разложение коллекции (кортежа, списка и т.д.) на отдельные значения.

Так, как и многие языки программирования, Python поддерживает концепцию множественного присваивания. Например:

x, y = 1, 2 print(x) # 1 print(y) # 2

В данном случае присваивем значения сразу двум переменным. Присвоение идет по позиции: переменная x получает значение 1, а переменная y — значени 2.

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

x, y = (1, 2) print(x) # 1 print(y) # 2

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

Подобным образом можно разложить другие кортежи, например:

name, age, company = ("Tom", 38, "Google") print(name) # Tom print(age) # 38 print(company) # Google

Только кортежами мы не ограничены и можем «распаковывать» и другие коллекции, например, списки:

people = ["Tom", "Bob", "Sam"] first, second, third = people print(first) # Tom print(second) # Bob print(third) # Sam

При разложении словаря переменные получают ключи словаря:

dictionary = r, b, g = dictionary print(r) # red print(b) # blue print(g) # green # получаем значение по ключу print(dictionary[g]) # зеленый

Деструктуризация в циклах

Циклы в Python позволяют разложить коллекции на отдельные составляющие:

people = [ ("Tom", 38, "Google"), ("Bob", 42, "Microsoft"), ("Sam", 29, "JetBrains") ] for name, age, company in people: print(f"Name: , Age: , Company: ")

Здесь мы перебираем список кортежей people. Каждый кортеж состоит из трех элементов, соответственно при переборе мы можем их передать в переменные name, age и company.

Другой пример — функция enumerate() . Она принимает в качестве параметра коллекцию, создает для каждого элемента кортеж и возвращает набор из подобных кортежей. Каждый кортеж содержит индекс, который увеличивается с каждой итерацией:

people = ["Tom", "Bob", "Sam"] for index, name in enumerate(people): print(f".") # результат # 0.Tom # 1.Bob # 2.Sam

Игнорирование значений

Если какой-то элемент коллекции не нужен, то обычно для него определяется переменная с именем _ (прочерк):

person =("Tom", 38, "Google") name, _, company = person print(name) # Tom print(company) # Google

Здесь нам не важен второй элемент кортежа, поэтому для него определяем переменную _. Хотя в реальности _ — такое же действительное имя, как name и company:

name, _, company = person print(_) # 38

Упаковка значений и оператор *

Оператор * упаковывает значение в коллекцию. Например:

num1=1 num2=2 num3=3 *numbers,=num1,num2,num3 print(numbers) #[1, 2, 3]

Здесь мы упаковываем значения из кортежа (num1,num2,num3) в список numbers. Причем, чтобы получить список, после numbers указывается запятая.

Как правило, упаковка применяется для сбора значений, которые остались после присвоения результатов деструктуризации. Например:

head, *tail = [1, 2, 3, 4, 5] print(head) # 1 print(tail) # [2, 3, 4, 5]

Здесь переменная head в соответствии с позицией получае первый элемент списка. Все остальные элементы передаются в переменную tail . Таким образом, переменная tail будет представлять список из оставшихся элементов.

Аналогичным образом можно получить все кроме последнего:

*head, tail = [1, 2, 3, 4, 5] print(head) # [1, 2, 3, 4] print(tail) # 5

Или элементы по середине, кроме первого и последнего:

head, *middle, tail = [1, 2, 3, 4, 5] print(head) # 1 print(middle) # [2, 3, 4] print(tail) # 5

Или все кроме первого и второго:

first, second, *other = [1, 2, 3, 4, 5] print(first) # 1 print(second) # 2 print(other) # [3, 4, 5]

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

Другой пример — нам надо получить только первый, третий и последний элемент, а остальные элементы нам не нужны. В общем случае мы должны предоставить переменные для всех элементов коллекции. Однако если коллекция имеет 100 элементов, а нам нужно только три, не будем же мы определять все сто переменных. И в этом случае опять же можно применить упаковку:

first, _, third, *_, last = [1, 2, 3, 4, 5, 6, 7, 8] print(first) # 1 print(third) # 3 print(last) # 8

Также можно получить ключи словаря:

red, *other, green = print(red) # red print(green) # green print(other) # ['blue', 'yellow']

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

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

nums1 = [1, 2, 3] nums2 = (4, 5, 6) # распаковываем список nums1 и кортеж nums2 nums3 = [*nums1, *nums2] print(nums3) # [1, 2, 3, 4, 5, 6]

Здесь распаковывем значения из списка nums1 и кортежа nums2 и помещаем их в список nums3.

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

dictionary1 = dictionary2 = # распаковываем словари dictionary3 = <**dictionary1, **dictionary2>print(dictionary3) #

Распаковка переменных#

Распаковка переменных — это специальный синтаксис, который позволяет присваивать переменным элементы итерируемого объекта.

Достаточно часто этот функционал встречается под именем tuple unpacking, но распаковка работает на любом итерируемом объекте, не только с кортежами

Пример распаковки переменных:

In [1]: interface = ['FastEthernet0/1', '10.1.1.1', 'up', 'up'] In [2]: intf, ip, status, protocol = interface In [3]: intf Out[3]: 'FastEthernet0/1' In [4]: ip Out[4]: '10.1.1.1' 

Такой вариант намного удобней использовать, чем использование индексов:

In [5]: intf, ip, status, protocol = interface[0], interface[1], interface[2], interface[3] 

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

Если переменных больше или меньше, возникнет исключение:

In [6]: intf, ip, status = interface ------------------------------------------------------------ ValueError Traceback (most recent call last) ipython-input-11-a304c4372b1a> in module>() ----> 1 intf, ip, status = interface ValueError: too many values to unpack (expected 3) In [7]: intf, ip, status, protocol, other = interface ------------------------------------------------------------ ValueError Traceback (most recent call last) ipython-input-12-ac93e78b978c> in module>() ----> 1 intf, ip, status, protocol, other = interface ValueError: not enough values to unpack (expected 5, got 4) 

Замена ненужных элементов _ #

Часто из всех элементов итерируемого объекта нужны только некоторые. При этом синтаксис распаковки требует указать ровно столько переменных, сколько элементов в итерируемом объекте.

Если, например, из строки line надо получить только VLAN, MAC и интерфейс, надо все равно указать переменную для типа записи:

In [8]: line = '100 01bb.c580.7000 DYNAMIC Gi0/1' In [9]: vlan, mac, item_type, intf = line.split() In [10]: vlan Out[10]: '100' In [11]: intf Out[11]: 'Gi0/1' 

Если тип записи не нужен в дальнейшем, можно заменить переменную item_type нижним подчеркиванием:

In [12]: vlan, mac, _, intf = line.split() 

Таким образом явно указывается то, что этот элемент не нужен.

Нижнее подчеркивание можно использовать и несколько раз:

In [13]: dhcp = '00:09:BB:3D:D6:58 10.1.10.2 86250 dhcp-snooping 10 FastEthernet0/1' In [14]: mac, ip, _, _, vlan, intf = dhcp.split() In [15]: mac Out[15]: '00:09:BB:3D:D6:58' In [16]: vlan Out[16]: '10' 

Использование * #

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

Например, так можно получить первый элемент в переменную first, а остальные в rest:

In [18]: vlans = [10, 11, 13, 30] In [19]: first, *rest = vlans In [20]: first Out[20]: 10 In [21]: rest Out[21]: [11, 13, 30] 

При этом переменная со звездочкой всегда будет содержать список:

In [22]: vlans = (10, 11, 13, 30) In [22]: first, *rest = vlans In [23]: first Out[23]: 10 In [24]: rest Out[24]: [11, 13, 30] 

Если элемент всего один, распаковка все равно отработает:

In [25]: first, *rest = vlans In [26]: first Out[26]: 55 In [27]: rest Out[27]: [] 

Такая переменная со звездочкой в выражении распаковки может быть только одна.

In [28]: vlans = (10, 11, 13, 30) In [29]: first, *rest, *others = vlans File "", line 1 first, *rest, *others = vlans ^ SyntaxError: two starred expressions in assignment 

Такая переменная может находиться не только в конце выражения:

In [30]: vlans = (10, 11, 13, 30) In [31]: *rest, last = vlans In [32]: rest Out[32]: [10, 11, 13] In [33]: last Out[33]: 30 

Таким образом можно указать, что нужен первый, второй и последний элемент:

In [34]: cdp = 'SW1 Eth 0/0 140 S I WS-C3750- Eth 0/1' In [35]: name, l_intf, *other, r_intf = cdp.split() In [36]: name Out[36]: 'SW1' In [37]: l_intf Out[37]: 'Eth' In [38]: r_intf Out[38]: '0/1' 

Примеры распаковки#

Распаковка итерируемых объектов#

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

In [39]: first, *rest = range(1, 6) In [40]: first Out[40]: 1 In [41]: rest Out[41]: [2, 3, 4, 5] 
In [42]: a = [1, 2, 3, 4, 5] In [43]: b = [100, 200, 300, 400, 500] In [44]: zip(a, b) Out[44]: zip at 0xb4df4fac> In [45]: list(zip(a, b)) Out[45]: [(1, 100), (2, 200), (3, 300), (4, 400), (5, 500)] In [46]: first, *rest, last = zip(a, b) In [47]: first Out[47]: (1, 100) In [48]: rest Out[48]: [(2, 200), (3, 300), (4, 400)] In [49]: last Out[49]: (5, 500) 

Пример распаковки в цикле for#

Пример цикла, который проходится по ключам:

In [50]: access_template = ['switchport mode access', . : 'switchport access vlan', . : 'spanning-tree portfast', . : 'spanning-tree bpduguard enable'] . : In [51]: access = '0/12': 10, '0/14': 11, '0/16': 17> In [52]: for intf in access: . : print(f'interface FastEthernet intf>') . : for command in access_template: . : if command.endswith('access vlan'): . : print(' <> <>'.format(command, access[intf])) . : else: . : print(' <>'.format(command)) . : interface FastEthernet0/12 switchport mode access switchport access vlan 10 spanning-tree portfast spanning-tree bpduguard enable interface FastEthernet0/14 switchport mode access switchport access vlan 11 spanning-tree portfast spanning-tree bpduguard enable interface FastEthernet0/16 switchport mode access switchport access vlan 17 spanning-tree portfast spanning-tree bpduguard enable 

Вместо этого можно проходиться по парам ключ-значение и сразу же распаковывать их в разные переменные:

In [53]: for intf, vlan in access.items(): . : print(f'interface FastEthernet intf>') . : for command in access_template: . : if command.endswith('access vlan'): . : print(' <> <>'.format(command, vlan)) . : else: . : print(' <>'.format(command)) . : 

Пример распаковки элементов списка в цикле:

In [54]: table Out[54]: [['100', 'a1b2.ac10.7000', 'DYNAMIC', 'Gi0/1'], ['200', 'a0d4.cb20.7000', 'DYNAMIC', 'Gi0/2'], ['300', 'acb4.cd30.7000', 'DYNAMIC', 'Gi0/3'], ['100', 'a2bb.ec40.7000', 'DYNAMIC', 'Gi0/4'], ['500', 'aa4b.c550.7000', 'DYNAMIC', 'Gi0/5'], ['200', 'a1bb.1c60.7000', 'DYNAMIC', 'Gi0/6'], ['300', 'aa0b.cc70.7000', 'DYNAMIC', 'Gi0/7']] In [55]: for line in table: . : vlan, mac, _, intf = line . : print(vlan, mac, intf) . : 100 a1b2.ac10.7000 Gi0/1 200 a0d4.cb20.7000 Gi0/2 300 acb4.cd30.7000 Gi0/3 100 a2bb.ec40.7000 Gi0/4 500 aa4b.c550.7000 Gi0/5 200 a1bb.1c60.7000 Gi0/6 300 aa0b.cc70.7000 Gi0/7 

Но еще лучше сделать так:

In [56]: for vlan, mac, _, intf in table: . : print(vlan, mac, intf) . : 100 a1b2.ac10.7000 Gi0/1 200 a0d4.cb20.7000 Gi0/2 300 acb4.cd30.7000 Gi0/3 100 a2bb.ec40.7000 Gi0/4 500 aa4b.c550.7000 Gi0/5 200 a1bb.1c60.7000 Gi0/6 300 aa0b.cc70.7000 Gi0/7 

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

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