До сих пор, весь код выполнялся последовательно - все строки скрипта выполнялись в том порядке, в котором они записаны в файле. В этом разделе рассматриваются возможности Python в управлении ходом программы:
- ответвления в ходе программы с помощью конструкции if/elif/else
- повторение действий в цикле с помощью конструкций for и while
- обработка ошибок с помощью конструкции try/except
Конструкция if/elif/else позволяет делать ответвления в ходе программы. Программа уходит в ветку при выполнении определенного условия.
В этой конструкции только if является обязательным, elif и else опциональны:
- Проверка if всегда идет первой.
- После оператора if должно быть какое-то условие: если это условие выполняется (возвращает True), то действия в блоке if выполняются.
- С помощью elif можно сделать несколько разветвлений, то есть, проверять входящие данные на разные условия.
- Блок elif это тот же if, но только следующая проверка. Грубо говоря, это «а если …»
- Блоков elif может быть много.
- Блок else выполняется в том случае, если ни одно из условий if или elif не было истинным.
Пример конструкции:
Конструкция if построена на условиях: после if и elif всегда пишется условие. Блоки if/elif выполняются только когда условие возвращает True
В Python, кроме очевидных значений True и False, всем остальным объектам также соответствует ложное или истинное значение:
Примеры:
In [4]: list_to_test = 1, 2, 3 In [5]: if list_to_test: ...: print("В списке есть объекты") ...: В списке есть объекты
тот же результат получим:
In [6]: if len(list_to_test) != 0: ...: print("В списке есть объекты") ...: В списке есть объекты
> ,
Пример:
Оператор in позволяет выполнять проверку на наличие элемента в последовательности (например, элемента в списке или подстроки в строке):
In [8]: 'Fast' in 'FastEthernet' Out[8]: True In [9]: 'Gigabit' in 'FastEthernet' Out[9]: False In [10]: vlan = [10, 20, 30, 40] In [11]: 10 in vlan Out[11]: True In [12]: 50 in vlan Out[12]: False
При использовании со словарями условие in выполняет проверку по ключам словаря:
In [15]: r1 = { ....: 'IOS': '15.4', ....: 'IP': '10.255.0.1', ....: 'hostname': 'london_r1', ....: 'location': '21 New Globe Walk', ....: 'model': '4451', ....: 'vendor': 'Cisco'} In [16]: 'IOS' in r1 Out[16]: True In [17]: '4451' in r1 Out[17]: False
В условиях могут также использоваться логические операторы and, or, not:
In [15]: r1 = { ....: 'IOS': '15.4', ....: 'IP': '10.255.0.1', ....: 'hostname': 'london_r1', ....: 'location': '21 New Globe Walk', ....: 'model': '4451', ....: 'vendor': 'Cisco'} In [18]: vlan = [10, 20, 30, 40] In [19]: 'IOS' in r1 and 10 in vlan Out[19]: True In [20]: '4451' in r1 and 10 in vlan Out[20]: False In [21]: '4451' in r1 or 10 in vlan Out[21]: True In [22]: not '4451' in r1 Out[22]: True In [23]: '4451' not in r1 Out[23]: True
Пример скрипта check_password.py, который проверяет длину пароля и есть ли в пароле имя пользователя:
Иногда удобнее использовать тернарный оператор, нежели развернутую форму:
s = [1, 2, 3, 4] result = True if len(s) > 5 else False
Этим лучше не злоупотреблять, но в простых выражениях такая запись может быть полезной.
Очень часто одно и то же действие надо выполнить для набора однотипных данных.
Цикл for перебирает по одному элементы указанной последовательности и выполняет действия, которые указаны в блоке for, для каждого элемента.
Примеры последовательностей элементов, по которым может проходиться цикл for:
- строка
- список
- словарь
- Функция range
- любой Итерируемый объект
Пример преобразования строк в списке в верхний регистр
words = ['list', 'dict', 'tuple'] upper_words = [] for word in words: upper_words.append(word.upper()) print (upper_words)
Проект pythontutor может очень помочь в понимании циклов. Там есть специальная визуализация кода, которая позволяет увидеть, что происходит на каждом этапе выполнения кода, что особенно полезно на первых порах с циклами. На сайте pythontutor можно загружать свой код, но для примера, по этой ссылке можно посмотреть пример выше.
Аналогичным образом for работает с кортежами.
При работе со строками, цикл for перебирает символы строки, например:
In [1]: for letter in 'Test string': ...: print(letter)
Иногда в цикле необходимо использовать последовательность чисел. В этом случае, лучше всего использовать функцию Функция range
In [2]: for i in range(10): ...: print(f'interface FastEthernet0/{i}')
Ещё пример цикла:
In [3]: vlans = [10, 20, 30, 40, 100] In [4]: for vlan in vlans: ...: print(f'vlan {vlan}') ...: print(f' name VLAN_{vlan}') ...: vlan 10 name VLAN_10 vlan 20 name VLAN_20 vlan 30 name VLAN_30 vlan 40 name VLAN_40 vlan 100 name VLAN_100
Когда цикл идет по словарю, то фактически он проходится по ключам:
r1 = { 'ios': '15.4', 'ip': '10.255.0.1', 'hostname': 'london_r1', 'location': '21 New Globe Walk', 'model': '4451', 'vendor': 'Cisco'} for k in r1: print(k)
Если необходимо выводить пары ключ-значение в цикле, можно делать так:
for key in r1: print(key + ' => ' + r1[key])
Или воспользоваться методом items, который позволяет проходиться в цикле сразу по паре ключ-значение:
for key, value in r1.items(): print(key + ' => ' + value)
Метод items возвращает специальный объект view, который отображает пары ключ-значение:
In [38]: r1.items() Out[38]: dict_items([('ios', '15.4'), ('ip', '10.255.0.1'), ('hostname', 'london_r1'), ('location', '21 New Globe Walk'), ('model', '4451'), ('vendor', 'Cisco')])
Циклы for можно вкладывать друг в друга.
В этом примере в списке commands хранятся команды, которые надо выполнить для каждого из интерфейсов в списке fast_int:
In [7]: commands = ['switchport mode access', 'spanning-tree portfast', 'spanning-tree bpduguard enable'] In [8]: fast_int = ['0/1','0/3','0/4','0/7','0/9','0/10','0/11'] In [9]: for intf in fast_int: ...: print('interface FastEthernet {}'.format(intf)) ...: for command in commands: ...: print(' {}'.format(command)) ...: interface FastEthernet 0/1 switchport mode access spanning-tree portfast spanning-tree bpduguard enable interface FastEthernet 0/3 switchport mode access spanning-tree portfast spanning-tree bpduguard enable interface FastEthernet 0/4 switchport mode access spanning-tree portfast spanning-tree bpduguard enable
Файл generate_access_port_config.py:
access_template = ['switchport mode access', 'switchport access vlan', 'spanning-tree portfast', 'spanning-tree bpduguard enable'] access = {'0/12': 10, '0/14': 11, '0/16': 17, '0/17': 150} 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))
Комментарии к коду:
В первом цикле for перебираются ключи и значения во вложенном словаре access
Текущий ключ, на данный момент цикла, хранится в переменной intf
Текущее значение, на данный момент цикла, хранится в переменной vlan
Выводится строка interface FastEthernet с добавлением к ней номера интерфейса
Во втором цикле for перебираются команды из списка access_template
Так как к команде switchport access vlan надо добавить номер VLAN:
внутри второго цикла for проверяются команды
если команда заканчивается на access vlan
выводится команда, и к ней добавляется номер VLAN
во всех остальных случаях просто выводится команда
Результат выполнения скрипта:
$ python generate_access_port_config.py 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 interface FastEthernet0/17 switchport mode access switchport access vlan 150 spanning-tree portfast spanning-tree bpduguard enable
Цикл while - это еще одна разновидность цикла в Python.
В цикле while, как и в выражении if, надо писать условие. Если условие истинно, выполняются действия внутри блока while. При этом, в отличие от if, после выполнения кода в блоке, while возвращается в начало цикла.
При использовании циклов while необходимо обращать внимание на то, будет ли достигнуто такое состояние, при котором условие цикла будет ложным.
In [1]: a = 5 In [2]: while a > 0: ...: print(a) ...: a -= 1 # Эта запись равнозначна a = a - 1 ...: 5 4 3 2 1
Файл check_password_with_while.py:
Оператор break позволяет досрочно прервать цикл:
- break прерывает текущий цикл и продолжает выполнение следующих выражений
- если используется несколько вложенных циклов, break прерывает внутренний цикл и продолжает выполнять выражения, следующие за блоком * break может использоваться в циклах for и while
Пример с циклом while:
Использование break в примере с запросом пароля (файл check_password_with_while_break.py):
Оператор continue возвращает управление в начало цикла. То есть, continue позволяет «перепрыгнуть» оставшиеся выражения в цикле и перейти к следующей итерации.
In [4]: for num in range(5): ...: if num == 3: ...: continue ...: else: ...: print(num) ...: 0 1 2 4
In [5]: i = 0 In [6]: while i
Использование continue в примере с запросом пароля (файл check_password_with_while_continue.py):
Тут выход из цикла выполнятся с помощью проверки флага password_correct. Когда был введен правильный пароль, флаг выставляется равным True, и с помощью continue выполняется переход в начало цикла, перескочив последнюю строку с запросом пароля.
Оператор pass ничего не делает. Фактически, это такая заглушка для объектов.
Например, pass может помочь в ситуации, когда нужно прописать структуру скрипта. Его можно ставить в циклах, функциях, классах. И это не будет влиять на исполнение кода.
Пример использования pass:
- блок else выполняется в том случае, если цикл завершил итерацию списка
- но else не выполняется, если в цикле был выполнен break
Пример цикла for с else (блок else выполняется после завершения цикла for):
In [1]: for num in range(5): ....: print(num) ....: else: ....: print("Числа закончились") ....: 0 1 2 3 4 Числа закончились
Пример цикла for с else и break в цикле (из-за break блок else не выполняется):
In [2]: for num in range(5): ....: if num == 3: ....: break ....: else: ....: print(num) ....: else: ....: print("Числа закончились") ....: 0 1 2
Пример цикла for с else и continue в цикле (continue не влияет на блок else):
In [3]: for num in range(5): ....: if num == 3: ....: continue ....: else: ....: print(num) ....: else: ....: print("Числа закончились") ....: 0 1 2 4 Числа закончились
В цикле while:
- блок else выполняется в том случае, если цикл завершил итерацию списка
- но else не выполняется, если в цикле был выполнен break
Пример цикла while с else (блок else выполняется после завершения цикла while):
Пример цикла while с else и break в цикле (из-за break блок else не выполняется):
Даже если код синтаксически написан правильно, могут возникать ошибки. В Python эти ошибки называются исключения (exceptions).
Примеры исключений:
In [1]: 2/0 ----------------------------------------------------- ZeroDivisionError: division by zero In [2]: 'test' + 2 ----------------------------------------------------- TypeError: must be str, not int
Python позволяет работать с исключениями. Их можно перехватывать и выполнять определенные действия в том случае, если возникло исключение.
Для работы с исключениями используется конструкция try/except:
In [3]: try: ...: 2/0 ...: except ZeroDivisionError: ...: print("You can't divide by zero") ...: You can't divide by zero
Конструкция try работает таким образом:
- сначала выполняются выражения, которые записаны в блоке try
- если при выполнения блока try не возникло никаких исключений, блок except пропускается, и выполняется дальнейший код
- если во время выполнения блока try в каком-то месте возникло исключение, оставшаяся часть блока try пропускается
- если в блоке except указано исключение, которое возникло, выполняется код в блоке except
- если исключение, которое возникло, не указано в блоке except, выполнение программы прерывается и выдается ошибка
Обратите внимание, что строка Cool! в блоке try не выводится:
In [4]: try: ...: print("Let's divide some numbers") ...: 2/0 ...: print('Cool!') ...: except ZeroDivisionError: ...: print("You can't divide by zero") ...: Let's divide some numbers You can't divide by zero
В конструкции try/except может быть много except, если нужны разные действия в зависимости от типа ошибки.
Например, скрипт divide.py делит два числа введенных пользователем:
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") print("Результат: ", int(a)/int(b)) except ValueError: print("Пожалуйста, вводите только числа") except ZeroDivisionError: print("На ноль делить нельзя")
Если нет необходимости выводить различные сообщения на ошибки ValueError и ZeroDivisionError, можно сделать так (файл divide_ver2.py):
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") print("Результат: ", int(a)/int(b)) except (ValueError, ZeroDivisionError): print("Что-то пошло не так...")
В конструкции try/except есть опциональный блок else. Он выполняется в том случае, если не было исключения.
Например, если необходимо выполнять в дальнейшем какие-то операции с данными, которые ввел пользователь, можно записать их в блоке else (файл divide_ver3.py):
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") result = int(a)/int(b) except (ValueError, ZeroDivisionError): print("Что-то пошло не так...") else: print("Результат в квадрате: ", result**2)
Пример выполнения:
$ python divide_ver3.py Введите первое число: 10 Введите второе число: 2 Результат в квадрате: 25 $ python divide_ver3.py Введите первое число: werq Введите второе число: 3 Что-то пошло не так...
Блок finally - это еще один опциональный блок в конструкции try. Он выполняется всегда, независимо от того, было ли исключение или нет.
Сюда ставятся действия, которые надо выполнить в любом случае. Например, это может быть закрытие файла.
Файл divide_ver4.py с блоком finally:
# -*- coding: utf-8 -*- try: a = input("Введите первое число: ") b = input("Введите второе число: ") result = int(a)/int(b) except (ValueError, ZeroDivisionError): print("Что-то пошло не так...") else: print("Результат в квадрате: ", result**2) finally: print("Вот и сказочке конец, а кто слушал - молодец.")
Проверка:
$ python divide_ver4.py Введите первое число: 10 Введите второе число: 2 Результат в квадрате: 25 Вот и сказочке конец, а кто слушал - молодец. $ python divide_ver4.py Введите первое число: qwerewr Введите второе число: 3 Что-то пошло не так... Вот и сказочке конец, а кто слушал - молодец. $ python divide_ver4.py Введите первое число: 4 Введите второе число: 0 Что-то пошло не так... Вот и сказочке конец, а кто слушал - молодец.
Как правило, один и тот же код можно написать и с использованием исключений, и без них.
Например, этот вариант кода:
while True: a = input("Введите число: ") b = input("Введите второе число: ") try: result = int(a)/int(b) except ValueError: print("Поддерживаются только числа") except ZeroDivisionError: print("На ноль делить нельзя") else: print(result) break
Можно переписать таким образом без try/except (файл try_except_divide.py):
while True: a = input("Введите число: ") b = input("Введите второе число: ") if a.isdigit() and b.isdigit(): if int(b) == 0: print("На ноль делить нельзя") else: print(int(a)/int(b)) break else: print("Поддерживаются только числа")
Далеко не всегда аналогичный вариант без использования исключений будет простым и понятным.
Важно в каждой конкретной ситуации оценивать, какой вариант кода более понятный, компактный и универсальный - с исключениями или без.
Если вы раньше использовали какой-то другой язык программирования, есть вероятность, что в нём использование исключений считалось плохим тоном. В Python это не так.
Иногда в коде надо сгенерировать исключение, это можно сделать так:
raise ValueError("При выполнении команды возникла ошибка")
В Python есть много встроенных исключений, каждое из которых генерируется в определенной ситуации.
Например, TypeError обычно генерируется когда ожидался один тип данных, а передали другой
In [1]: "a" + 3 --------------------------------------------------------------------------- TypeError Traceback (most recent call last)in ----> 1 "a" + 3 TypeError: can only concatenate str (not "int") to str
Список mac содержит MAC-адреса в формате XXXX:XXXX:XXXX. Однако, в оборудовании cisco MAC-адреса используются в формате XXXX.XXXX.XXXX.
Написать код, который преобразует MAC-адреса в формат cisco и добавляет их в новый список result. Полученный список result вывести на стандартный поток вывода (stdout) с помощью print.
mac = ["aabb:cc80:7000", "aabb:dd80:7340", "aabb:ee80:7000", "aabb:ff80:7000"]
Решение
task_6_1.py
- Запросить у пользователя ввод IP-адреса в формате 10.0.1.1
- В зависимости от типа адреса (описаны ниже), вывести на стандартный поток вывода:
«unicast» - если первый байт в диапазоне 1-223
«multicast» - если первый байт в диапазоне 224-239
«local broadcast» - если IP-адрес равен 255.255.255.255
«unassigned» - если IP-адрес равен 0.0.0.0
«unused» - во всех остальных случаях
Решение
task_6_2.py
Сделать копию скрипта задания 6.2.
Добавить проверку введенного IP-адреса. Адрес считается корректно заданным, если он:
- состоит из 4 чисел (а не букв или других символов)
- числа разделенны точкой
- каждое число в диапазоне от 0 до 255
Если адрес задан неправильно, выводить сообщение: «Неправильный IP-адрес». Сообщение «Неправильный IP-адрес» должно выводиться только один раз, даже если несколько пунктов выше не выполнены.
Решение
task_6_2a.py
Сделать копию скрипта задания 6.2a.
Дополнить скрипт: Если адрес был введен неправильно, запросить адрес снова.
Решение
task_6_2b.py
В скрипте сделан генератор конфигурации для access-портов. Сделать аналогичный генератор конфигурации для портов trunk.
В транках ситуация усложняется тем, что VLANов может быть много, и надо понимать, что с ним делать. Поэтому в соответствии каждому порту стоит список и первый (нулевой) элемент списка указывает как воспринимать номера VLAN, которые идут дальше.
Пример значения и соответствующей команды:
[«add», «10», «20»] - команда switchport trunk allowed vlan add 10,20
[«del», «17»] - команда switchport trunk allowed vlan remove 17
[«only», «11», «30»] - команда switchport trunk allowed vlan 11,30
Задача для портов 0/1, 0/2, 0/4:
- сгенерировать конфигурацию на основе шаблона trunk_template
- с учетом ключевых слов add, del, only
Код не должен привязываться к конкретным номерам портов. То есть, если в словаре trunk будут другие номера интерфейсов, код должен работать.
Для данных в словаре trunk_template вывод на стандартный поток вывода должен быть таким:
interface FastEthernet 0/1 switchport trunk encapsulation dot1q switchport mode trunk switchport trunk allowed vlan add 10,20 interface FastEthernet 0/2 switchport trunk encapsulation dot1q switchport mode trunk switchport trunk allowed vlan 11,30 interface FastEthernet 0/4 switchport trunk encapsulation dot1q switchport mode trunk switchport trunk allowed vlan remove 17
Решение
task_6_3.py
Добавить комментарий