Вы здесь

Python. 2.2 Полезные функции

Функция print

Функция print уже не раз использовалась в книге, но до сих пор не рассматривался ее полный синтаксис:

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

In [8]: print(1, 2, 3) 1 2 3

Можно изменить значение sep на любую другую строку:

In [9]: print(1, 2, 3, sep='|') 1|2|3 In [10]: print(1, 2, 3, sep='\n') 1 2 3 In [11]: print(1, 2, 3, sep=f"\n{'-' * 10}\n") 1 ---------- 2 ---------- 3

В некоторых ситуациях функция print может заменить метод join:

In [16]: vlans = ['10', '20', '30'] In [17]: ','.join(vlans) Out[17]: '10,20,30'
In [12]: items = [1, 2, 3, 4, 5] In [13]: print(*items, sep=', ') 1, 2, 3, 4, 5


end

Параметр end контролирует то, какое значение выведется после вывода всех элементов. По умолчанию используется перевод строки:

In [20]: print(1, 2, 3, end='\n'+'-'*10) 1 2 3 ----------

file
Параметр file контролирует то, куда выводятся значения функции print. По умолчанию все выводится на стандартный поток вывода - sys.stdout.

Python позволяет передавать file как аргумент любой объект с методом write(string). За счет этого с помощью print можно записывать строки в файл:

In [1]: f = open('result.txt', 'w') In [2]: for num in range(10): ...: print(f'Item {num}', file=f) ...: In [3]: f.close() In [4]: cat result.txt Item 0 Item 1 Item 2 Item 3 Item 4 Item 5 Item 6 Item 7 Item 8 Item 9

flush
По умолчанию при записи в файл или выводе на стандартный поток вывода вывод буферизируется. Параметр flush позволяет отключать буферизацию.

Функция range

Функция range возвращает неизменяемую последовательность чисел в виде объекта range.
Синтаксис функции:

range(stop) range(start, stop[, step])

Параметры функции:
- start - с какого числа начинается последовательность. По умолчанию - 0
- stop - до какого числа продолжается последовательность чисел. Указанное число не включается в диапазон
- step - с каким шагом растут числа. По умолчанию 1

Функция range хранит только информацию о значениях start, stop и step и вычисляет значения по мере необходимости. Это значит, что независимо от размера диапазона, который описывает функция range, она всегда будет занимать фиксированный объем памяти.

Самый простой вариант range - передать только значение stop:

In [1]: range(5) Out[1]: range(0, 5) In [2]: list(range(5)) Out[2]: [0, 1, 2, 3, 4]

Если передаются два аргумента, то первый используется как start, а второй - как stop:

In [3]: list(range(1, 5)) Out[3]: [1, 2, 3, 4]

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

С помощью range можно генерировать и убывающие последовательности чисел:
In [6]: list(range(10, 0, -1)) Out[6]: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] In [7]: list(range(5, -1, -1)) Out[7]: [5, 4, 3, 2, 1, 0]

Объект range поддерживает все операции, которые поддерживают последовательности в Python, кроме сложения и умножения.
Проверка, входит ли число в диапазон, который описывает range:

In [11]: nums = range(5) In [12]: nums Out[12]: range(0, 5) In [13]: 3 in nums Out[13]: True In [14]: 7 in nums Out[14]: False

Можно получить конкретный элемент диапазона:

In [15]: nums = range(5) In [16]: nums[0] Out[16]: 0 In [17]: nums[-1] Out[17]: 4

Range поддерживает срезы:

In [18]: nums = range(5) In [19]: nums[1:] Out[19]: range(1, 5) In [20]: nums[:3] Out[20]: range(0, 3)

Можно получить длину диапазона:

In [21]: nums = range(5) In [22]: len(nums) Out[22]: 5

А также минимальный и максимальный элемент:

In [23]: nums = range(5) In [24]: min(nums) Out[24]: 0 In [25]: max(nums) Out[25]: 4

Кроме того, объект range поддерживает метод index:

In [26]: nums = range(1, 7) In [27]: nums.index(3) Out[27]: 2

Функция sorted

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

Первый аспект, на который важно обратить внимание - sorted всегда возвращает список.

Если сортировать список элементов, то возвращается новый список:

In [1]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [2]: sorted(list_of_words) Out[2]: ['', 'dict', 'list', 'one', 'two']

При сортировке кортежа также возвращается список:

In [3]: tuple_of_words = ('one', 'two', 'list', '', 'dict') In [4]: sorted(tuple_of_words) Out[4]: ['', 'dict', 'list', 'one', 'two']

Сортировка множества:

In [5]: set_of_words = {'one', 'two', 'list', '', 'dict'} In [6]: sorted(set_of_words) Out[6]: ['', 'dict', 'list', 'one', 'two']

Сортировка строки:

In [7]: string_to_sort = 'long string' In [8]: sorted(string_to_sort) Out[8]: [' ', 'g', 'g', 'i', 'l', 'n', 'n', 'o', 'r', 's', 't']

Если передать sorted словарь, функция вернет отсортированный список ключей:

In [9]: dict_for_sort = { ...: 'id': 1, ...: 'name': 'London', ...: 'IT_VLAN': 320, ...: 'User_VLAN': 1010, ...: 'Mngmt_VLAN': 99, ...: 'to_name': None, ...: 'to_id': None, ...: 'port': 'G1/0/11' ...: } In [10]: sorted(dict_for_sort) Out[10]: ['IT_VLAN', 'Mngmt_VLAN', 'User_VLAN', 'id', 'name', 'port', 'to_id', 'to_name']

reverse

Флаг reverse позволяет управлять порядком сортировки. По умолчанию сортировка будет по возрастанию элементов.

Указав флаг reverse, можно поменять порядок:

In [11]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [12]: sorted(list_of_words) Out[12]: ['', 'dict', 'list', 'one', 'two'] In [13]: sorted(list_of_words, reverse=True) Out[13]: ['two', 'one', 'list', 'dict', '']

key

С помощью параметра key можно указывать, как именно выполнять сортировку. Параметр key ожидает функцию, с помощью которой должно быть выполнено сравнение.

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

In [14]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [15]: sorted(list_of_words, key=len) Out[15]: ['', 'one', 'two', 'list', 'dict']

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

In [16]: dict_for_sort = { ...: 'id': 1, ...: 'name':'London', ...: 'IT_VLAN':320, ...: 'User_VLAN':1010, ...: 'Mngmt_VLAN':99, ...: 'to_name': None, ...: 'to_id': None, ...: 'port':'G1/0/11' ...: } In [17]: sorted(dict_for_sort, key=str.lower) Out[17]: ['id', 'IT_VLAN', 'Mngmt_VLAN', 'name', 'port', 'to_id', 'to_name', 'User_VLAN']

Параметру key можно передавать любые функции, не только встроенные. Также тут удобно использовать анонимную функцию lambda.

С помощью параметра key можно сортировать объекты не по первому элементу, а по любому другому. Но для этого надо использовать или функцию lambda, или специальные функции из модуля operator.

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

In [18]: from operator import itemgetter In [19]: list_of_tuples = [('IT_VLAN', 320), ...: ('Mngmt_VLAN', 99), ...: ('User_VLAN', 1010), ...: ('DB_VLAN', 11)] In [20]: sorted(list_of_tuples, key=itemgetter(1)) Out[20]: [('DB_VLAN', 11), ('Mngmt_VLAN', 99), ('IT_VLAN', 320), ('User_VLAN', 1010)]

Пример сортировки разных объектов

Сортировка выполняется по первому элементу, например, по первому символу в списке строк, если он одинаковый, по второму и так далее. Сортировка выполняется по коду Unicode символа. Для символов из одного алфавита, это значит что сортировка по сути будет по алфавиту.

Пример сортировки списка строк:

In [6]: data = ["test1", "test2", "text1", "text2"] In [7]: sorted(data) Out[7]: ['test1', 'test2', 'text1', 'text2']

Некоторые данные будут сортироваться неправильно, например, список IP-адресов:

In [11]: ip_list = ["10.1.1.1", "10.1.10.1", "10.1.2.1", "10.1.11.1"] In [12]: sorted(ip_list) Out[12]: ['10.1.1.1', '10.1.10.1', '10.1.11.1', '10.1.2.1']

Это происходит потому используется лексикографическая сортировка. Чтобы в данном случае сортировка была нормальной, надо или использовать отдельный модуль с натуральной сортировкой (модуль natsort) или сортировать, например, по двоичному/десятичному значению адреса.

Пример сортировки IP-адресов по двоичному значению. Сначала создаем функцию, которая преобразует IP-адреса в двоичный формат:

In [15]: def bin_ip(ip): ...: octets = [int(o) for o in ip.split(".")] ...: return ("{:08b}"*4).format(*octets) ...: In [16]: bin_ip("10.1.1.1") Out[16]: '00001010000000010000000100000001' In [17]: bin_ip("160.1.1.1") Out[17]: '10100000000000010000000100000001'

Сортировка с использованием функции bin_ip:

In [18]: ip_list = ["10.1.1.1", "10.1.10.1", "10.1.2.1", "10.1.11.1"] In [19]: sorted(ip_list, key=bin_ip) Out[19]: ['10.1.1.1', '10.1.2.1', '10.1.10.1', '10.1.11.1']

enumerate

Иногда, при переборе объектов в цикле for, нужно не только получить сам объект, но и его порядковый номер. Это можно сделать, создав дополнительную переменную, которая будет расти на единицу с каждым прохождением цикла. Однако, гораздо удобнее это делать с помощью итератора enumerate.

In [15]: list1 = ['str1', 'str2', 'str3'] In [16]: for position, string in enumerate(list1): ...: print(position, string) ...: 0 str1 1 str2 2 str3

enumerate умеет считать не только с нуля, но и с любого значение, которое ему указали после объекта:

In [17]: list1 = ['str1', 'str2', 'str3'] In [18]: for position, string in enumerate(list1, 100): ...: print(position, string) ...: 100 str1 101 str2 102 str3

Иногда нужно проверить, что сгенерировал итератор, как правило, на стадии написания скрипта. Если необходимо увидеть содержимое, которое сгенерирует итератор, полностью, можно воспользоваться функцией list:

In [19]: list1 = ['str1', 'str2', 'str3'] In [20]: list(enumerate(list1, 100)) Out[20]: [(100, 'str1'), (101, 'str2'), (102, 'str3')]

Пример использования enumerate для EEM

В этом примере используется Cisco EEM. Если в двух словах, то EEM позволяет выполнять какие-то действия (action) в ответ на событие (event).
Выглядит applet EEM так:

event manager applet Fa0/1_no_shut event syslog pattern "Line protocol on Interface FastEthernet0/0, changed state to down" action 1 cli command "enable" action 2 cli command "conf t" action 3 cli command "interface fa0/1" action 4 cli command "no sh"

В EEM, в ситуации, когда действий выполнить нужно много, неудобно каждый раз набирать action x cli command. Плюс, чаще всего, уже есть готовый кусок конфигурации, который должен выполнить EEM.

С помощью простого Python-скрипта можно сгенерировать команды EEM на основании существующего списка команд (файл enumerate_eem.py):

import sys config = sys.argv[1] with open(config, 'r') as f: for i, command in enumerate(f, 1): print('action {:04} cli command "{}"'.format(i, command.rstrip()))

В данном примере команды считываются из файла, а затем к каждой строке добавляется приставка, которая нужна для EEM.

Файл с командами выглядит так (r1_config.txt):

en conf t no int Gi0/0/0.300 no int Gi0/0/0.301 no int Gi0/0/0.302 int range gi0/0/0-2 channel-group 1 mode active interface Port-channel1.300 encapsulation dot1Q 300 vrf forwarding Management ip address 10.16.19.35 255.255.255.248

Вывод будет таким:

$ python enumerate_eem.py r1_config.txt action 0001 cli command "en" action 0002 cli command "conf t" action 0003 cli command "no int Gi0/0/0.300" action 0004 cli command "no int Gi0/0/0.301" action 0005 cli command "no int Gi0/0/0.302" action 0006 cli command "int range gi0/0/0-2" action 0007 cli command " channel-group 1 mode active" action 0008 cli command "interface Port-channel1.300" action 0009 cli command " encapsulation dot1Q 300" action 0010 cli command " vrf forwarding Management" action 0011 cli command " ip address 10.16.19.35 255.255.255.248"

Функция zip

Функция zip:
- на вход функции передаются последовательности
- zip возвращает итератор с кортежами, в котором n-ый кортеж состоит из n-ых элементов последовательностей, которые были переданы как аргументы
- например, десятый кортеж будет содержать десятый элемент каждой из переданных последовательностей
- если на вход были переданы последовательности разной длины, то все они будут отрезаны по самой короткой последовательности
- порядок элементов соблюдается

Так как zip - это итератор, для отображение его содержимого используется list
Пример использования zip:

In [1]: a = [1, 2, 3] In [2]: b = [100, 200, 300] In [3]: list(zip(a, b)) Out[3]: [(1, 100), (2, 200), (3, 300)]

Использование zip со списками разной длины:

In [4]: a = [1, 2, 3, 4, 5] In [5]: b = [10, 20, 30, 40, 50] In [6]: c = [100, 200, 300] In [7]: list(zip(a, b, c)) Out[7]: [(1, 10, 100), (2, 20, 200), (3, 30, 300)]

Использование zip для создания словаря

Пример использования zip для создания словаря:

In [4]: d_keys = ['hostname', 'location', 'vendor', 'model', 'IOS', 'IP'] In [5]: d_values = ['london_r1', '21 New Globe Walk', 'Cisco', '4451', '15.4', '10.255.0.1'] In [6]: list(zip(d_keys, d_values)) Out[6]: [('hostname', 'london_r1'), ('location', '21 New Globe Walk'), ('vendor', 'Cisco'), ('model', '4451'), ('IOS', '15.4'), ('IP', '10.255.0.1')] In [7]: dict(zip(d_keys, d_values)) Out[7]: {'IOS': '15.4', 'IP': '10.255.0.1', 'hostname': 'london_r1', 'location': '21 New Globe Walk', 'model': '4451', 'vendor': 'Cisco'} In [8]: r1 = dict(zip(d_keys,d_values)) In [9]: r1 Out[9]: {'IOS': '15.4', 'IP': '10.255.0.1', 'hostname': 'london_r1', 'location': '21 New Globe Walk', 'model': '4451', 'vendor': 'Cisco'}

В примере ниже есть отдельный список, в котором хранятся ключи, и словарь, в котором хранится в виде списка (чтобы сохранить порядок) информация о каждом устройстве.

Соберем их в словарь с ключами из списка и информацией из словаря data:

In [10]: d_keys = ['hostname', 'location', 'vendor', 'model', 'IOS', 'IP'] In [11]: data = { ....: 'r1': ['london_r1', '21 New Globe Walk', 'Cisco', '4451', '15.4', '10.255.0.1'], ....: 'r2': ['london_r2', '21 New Globe Walk', 'Cisco', '4451', '15.4', '10.255.0.2'], ....: 'sw1': ['london_sw1', '21 New Globe Walk', 'Cisco', '3850', '3.6.XE', '10.255.0.101'] ....: } In [12]: london_co = {} In [13]: for k in data.keys(): ....: london_co[k] = dict(zip(d_keys, data[k])) ....: In [14]: london_co Out[14]: {'r1': {'IOS': '15.4', 'IP': '10.255.0.1', 'hostname': 'london_r1', 'location': '21 New Globe Walk', 'model': '4451', 'vendor': 'Cisco'}, 'r2': {'IOS': '15.4', 'IP': '10.255.0.2', 'hostname': 'london_r2', 'location': '21 New Globe Walk', 'model': '4451', 'vendor': 'Cisco'}, 'sw1': {'IOS': '3.6.XE', 'IP': '10.255.0.101', 'hostname': 'london_sw1', 'location': '21 New Globe Walk', 'model': '3850', 'vendor': 'Cisco'}}

Функция all

Функция all возвращает True, если все элементы истинные (или объект пустой).

In [1]: all([False, True, True]) Out[1]: False In [2]: all([True, True, True]) Out[2]: True In [3]: all([]) Out[3]: True

Например, с помощью all можно проверить, все ли октеты в IP-адресе являются числами:

In [4]: ip = '10.0.1.1' In [5]: all(i.isdigit() for i in ip.split('.')) Out[5]: True In [6]: all(i.isdigit() for i in '10.1.1.a'.split('.')) Out[6]: False

Функция any

Функция any возвращает True, если хотя бы один элемент истинный.

In [7]: any([False, True, True]) Out[7]: True In [8]: any([False, False, False]) Out[8]: False In [9]: any([]) Out[9]: False In [10]: any(i.isdigit() for i in '10.1.1.a'.split('.')) Out[10]: True

Например, с помощью any, можно заменить функцию ignore_command:

def ignore_command(command): ''' Функция проверяет содержится ли в команде слово из списка ignore. * command - строка. Команда, которую надо проверить * Возвращает True, если в команде содержится слово из списка ignore, False - если нет ''' ignore = ['duplex', 'alias', 'Current configuration'] for word in ignore: if word in command: return True return False

На такой вариант:

def ignore_command(command): ''' Функция проверяет содержится ли в команде слово из списка ignore. command - строка. Команда, которую надо проверить Возвращает True, если в команде содержится слово из списка ignore, False - если нет ''' ignore = ['duplex', 'alias', 'Current configuration'] return any([word in command for word in ignore])

Анонимная функция (лямбда-выражение)

В Python лямбда-выражение позволяет создавать анонимные функции - функции, которые не привязаны к имени.

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

Стандартная функция:

In [1]: def sum_arg(a, b): return a + b In [2]: sum_arg(1, 2) Out[2]: 3

Аналогичная анонимная функция, или лямбда-функция:

In [3]: sum_arg = lambda a, b: a + b In [4]: sum_arg(1, 2) Out[4]: 3

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

Лямбда-функцию удобно использовать в выражениях, где требуется написать небольшую функцию для обработки данных.

Например, в функции sorted лямбда-выражение можно использовать для указания ключа для сортировки:

In [5]: list_of_tuples = [('IT_VLAN', 320), ...: ('Mngmt_VLAN', 99), ...: ('User_VLAN', 1010), ...: ('DB_VLAN', 11)] In [6]: sorted(list_of_tuples, key=lambda x: x[1]) Out[6]: [('DB_VLAN', 11), ('Mngmt_VLAN', 99), ('IT_VLAN', 320), ('User_VLAN', 1010)]

Также лямбда-функция пригодится в функциях map и filter, которые будут рассматриваться в следующих разделах.

Функция map

Функция map применяет функцию к каждому элементу последовательности и возвращает итератор с результатами.

Например, с помощью map можно выполнять преобразования элементов. Перевести все строки в верхний регистр:

In [1]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [2]: map(str.upper, list_of_words) Out[2]: In [3]: list(map(str.upper, list_of_words)) Out[3]: ['ONE', 'TWO', 'LIST', '', 'DICT']

Конвертация в числа:

In [3]: list_of_str = ['1', '2', '5', '10'] In [4]: list(map(int, list_of_str)) Out[4]: [1, 2, 5, 10]

Вместе с map удобно использовать лямбда-выражения:

In [5]: vlans = [100, 110, 150, 200, 201, 202] In [6]: list(map(lambda x: 'vlan {}'.format(x), vlans)) Out[6]: ['vlan 100', 'vlan 110', 'vlan 150', 'vlan 200', 'vlan 201', 'vlan 202']

Если функция, которую использует map(), ожидает два аргумента, то передаются два списка:
In [7]: nums = [1, 2, 3, 4, 5]

In [8]: nums2 = [100, 200, 300, 400, 500]

In [9]: list(map(lambda x, y: x*y, nums, nums2))
Out[9]: [100, 400, 900, 1600, 2500]

List comprehension вместо map

Как правило, вместо map можно использовать list comprehension. Чаще всего, вариант с list comprehension более понятный, а в некоторых случаях даже быстрее.

Но map может быть эффективней в том случае, когда надо сгенерировать большое количество элементов, так как map - итератор, а list comprehension генерирует список.

Примеры, аналогичные приведенным выше, в варианте с list comprehension.

Перевести все строки в верхний регистр:

In [48]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [49]: [word.upper() for word in list_of_words] Out[49]: ['ONE', 'TWO', 'LIST', '', 'DICT']

Конвертация в числа:

In [50]: list_of_str = ['1', '2', '5', '10'] In [51]: [int(i) for i in list_of_str] Out[51]: [1, 2, 5, 10]

Форматирование строк:

In [52]: vlans = [100, 110, 150, 200, 201, 202] In [53]: [f'vlan {x}' for x in vlans] Out[53]: ['vlan 100', 'vlan 110', 'vlan 150', 'vlan 200', 'vlan 201', 'vlan 202']

Для получения пар элементов используется zip:

In [54]: nums = [1, 2, 3, 4, 5] In [55]: nums2 = [100, 200, 300, 400, 500] In [56]: [x * y for x, y in zip(nums, nums2)] Out[56]: [100, 400, 900, 1600, 2500]

Функция filter

Функция filter применяет функцию ко всем элементам последовательности и возвращает итератор с теми объектами, для которых функция вернула True.

Например, вернуть только те строки, в которых находятся числа:

In [1]: list_of_strings = ['one', 'two', 'list', '', 'dict', '100', '1', '50'] In [2]: filter(str.isdigit, list_of_strings) Out[2]: In [3]: list(filter(str.isdigit, list_of_strings)) Out[3]: ['100', '1', '50']

Из списка чисел оставить только нечетные:

In [3]: list(filter(lambda x: x % 2 == 1, [10, 111, 102, 213, 314, 515])) Out[3]: [111, 213, 515]

Аналогично, только четные:

In [4]: list(filter(lambda x: x % 2 == 0, [10, 111, 102, 213, 314, 515])) Out[4]: [10, 102, 314]

Из списка слов оставить только те, у которых количество букв больше двух:

In [5]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [6]: list(filter(lambda x: len(x) > 2, list_of_words)) Out[6]: ['one', 'two', 'list', 'dict']

List comprehension вместо filter

Как правило, вместо filter можно использовать list comprehension.

Примеры, аналогичные приведенным выше, в варианте с list comprehension.

Вернуть только те строки, в которых находятся числа:

In [7]: list_of_strings = ['one', 'two', 'list', '', 'dict', '100', '1', '50'] In [8]: [s for s in list_of_strings if s.isdigit()] Out[8]: ['100', '1', '50']

Нечетные/четные числа:

In [9]: nums = [10, 111, 102, 213, 314, 515] In [10]: [n for n in nums if n % 2 == 1] Out[10]: [111, 213, 515] In [11]: [n for n in nums if n % 2 == 0] Out[11]: [10, 102, 314]

Из списка слов оставить только те, у которых количество букв больше двух:

In [12]: list_of_words = ['one', 'two', 'list', '', 'dict'] In [13]: [word for word in list_of_words if len(word) > 2] Out[13]: ['one', 'two', 'list', 'dict']

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

Filtered HTML

  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Допустимые HTML-теги: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Строки и абзацы переносятся автоматически.

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и абзацы переносятся автоматически.
CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.
Target Image