Распаковка переменных - это специальный синтаксис, который позволяет присваивать переменным элементы итерируемого объекта.
Достаточно часто этот функционал встречается под именем 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 "<ipython-input-37-dedf7a08933a>", 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'
Эти примеры показывают, что распаковывать можно не только списки, кортежи и строки, но и любой другой итерируемый объект.
Распаковка range:
In [39]: first, *rest = range(1,6) In [40]: first Out[40]: 1 In [41]: rest Out[41]: [2, 3, 4, 5]
Распаковка zip:
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)
Пример цикла, который проходится по ключам:
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('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('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