Обработка ошибок python яндекс практикум

Задача 1

Напишите функцию what_weather() (англ. «какая погода?»), которую затем будете использовать в коде Анфисы:

  • Выполните HTTP-запрос, поместив вызов функции get() внутрь блока try.
  • Значения URL и параметров получите из функций make_url() (в неё нужно передать нужный город как аргумент city) и make_parameters().
  • При «выбрасывании» исключения типа requests.ConnectionError (от англ. «ошибка соединения») — функция what_weather() должна возвращать сообщение об ошибке '<сетевая ошибка>'.
  • Если код HTTP-ответа равен 200 (всё хорошо), верните из функции текст ответа. В противном случае функция должна вернуть строку '<ошибка на сервере погоды>'.

Код:

import requests

cities = [
    'Омск',
    'Калининград',
    'Челябинск',
    'Владивосток',
    'Красноярск',
    'Москва',
    'Екатеринбург'
]

def make_url(city):
    # в URL задаём город, в котором узнаем погоду
    return f'http://wttr.in/{city}'

def make_parameters():
    params = {
        'format': 2,  # погода одной строкой
        'M': ''  # скорость ветра в "м/с"
    }
    return params

def what_weather(city):
    # Напишите тело этой функции.
    # Не изменяйте остальной код!
    try:
        request = requests.get(make_url(city), params=make_parameters())
        #print(request.status_code)
        if request.status_code == 200: return request.text
        else: return '<ошибка на сервере погоды>'
    except requests.ConnectionError : return '<сетевая ошибка>'

print('Погода в городах:')
for city in cities:
    print(city, what_weather(city))

Результат:

Погода в городах:
Омск ? ?️-7°C ?️↖4.2 m/s

Калининград ⛅️ ?️+2°C ?️↖6.1 m/s

Челябинск ? ?️-3°C ?️↘6.1 m/s

Владивосток ☀️ ?️-3°C ?️↓7.2 m/s

Красноярск ⛅️ ?️-10°C ?️→8.3 m/s

Москва ☀️ ?️-2°C ?️↑4.2 m/s

Екатеринбург ? ?️-2°C ?️↘4.2 m/s

Задача 2

Это задание — финальное.

В нём вы сделаете Анфису мастером на все руки.

Анфиса будет знать всё про ваших друзей — где они, сколько у них времени, и какая у них погода.В список запросов queries в функции runner() добавлены новые запросы про погоду:

  • Коля, как погода?
  • Соня, как погода?
  • Антон, как погода?

Научите Анфису отвечать на вопросы такого вида.

Для этого:

  1. Добавьте в функцию process_friend() обработку ещё одного запроса 'как погода?'. Для получения ответа на этот вопрос используйте значение city — это город, в котором живёт друг.
  2. Затем вызовите функцию what_weather() — вы написали на прошлом уроке почти такую же. Она уже доступна в коде этого задания.
  3. Верните результат выполнения этой функции как результат process_friend().

Код:

import datetime as dt
import requests

DATABASE = {
    'Сергей': 'Омск',
    'Соня': 'Москва',
    'Алексей': 'Калининград',
    'Миша': 'Москва',
    'Дима': 'Челябинск',
    'Алина': 'Красноярск',
    'Егор': 'Пермь',
    'Коля': 'Красноярск',
    'Артём': 'Владивосток',
    'Петя': 'Михайловка'
}

UTC_OFFSET = {
    'Москва': 3,
    'Санкт-Петербург': 3,
    'Новосибирск': 7,
    'Екатеринбург': 5,
    'Нижний Новгород': 3,
    'Казань': 3,
    'Челябинск': 5,
    'Омск': 6,
    'Самара': 4,
    'Ростов-на-Дону': 3,
    'Уфа': 5,
    'Красноярск': 7,
    'Воронеж': 3,
    'Пермь': 5,
    'Волгоград': 4,
    'Краснодар': 3,
    'Калининград': 2,
    'Владивосток': 10
}


def format_count_friends(count_friends):
    if count_friends == 1:
        return '1 друг'
    elif 2 <= count_friends <= 4:
        return f'{count_friends} друга'
    else:
        return f'{count_friends} друзей'


def what_time(city):
    offset = UTC_OFFSET[city]
    city_time = dt.datetime.utcnow() + dt.timedelta(hours=offset)
    f_time = city_time.strftime("%H:%M")
    return f_time


def what_weather(city):
    url = f'http://wttr.in/{city}'
    weather_parameters = {
        'format': 2,
        'M': ''
    }
    try:
        response = requests.get(url, params=weather_parameters)
    except requests.ConnectionError:
        return '<сетевая ошибка>'
    if response.status_code == 200:
        return response.text.strip()
    else:
        return '<ошибка на сервере погоды>'


def process_anfisa(query):
    if query == 'сколько у меня друзей?':
        count_string = format_count_friends(len(DATABASE))
        return f'У тебя {count_string}'
    elif query == 'кто все мои друзья?':
        friends_string = ', '.join(DATABASE.keys())
        return f'Твои друзья: {friends_string}'
    elif query == 'где все мои друзья?':
        unique_cities = set(DATABASE.values())
        cities_string = ', '.join(unique_cities)
        return f'Твои друзья в городах: {cities_string}'
    else:
        return '<неизвестный запрос>'


def process_friend(name, query):
    if name in DATABASE:
        city = DATABASE[name]
        if query == 'ты где?':
            return f'{name} в городе {city}'
        elif query == 'который час?':
            if city not in UTC_OFFSET:
                return f'<не могу определить время в городе {city}>'
            time = what_time(city)
            return f'Там сейчас {time}'
        elif query == 'как погода?':
            return what_weather(city)
            
        else:
            return '<неизвестный запрос>'
    else:
        return f'У тебя нет друга по имени {name}'


def process_query(query):
    tokens = query.split(', ')
    name = tokens[0]
    if name == 'Анфиса':
        return process_anfisa(tokens[1])
    else:
        return process_friend(name, tokens[1])


def runner():
    queries = [
        'Анфиса, сколько у меня друзей?',
        'Анфиса, кто все мои друзья?',
        'Анфиса, где все мои друзья?',
        'Анфиса, кто виноват?',
        'Коля, ты где?',
        'Соня, что делать?',
        'Антон, ты где?',
        'Алексей, который час?',
        'Артём, который час?',
        'Антон, который час?',
        'Петя, который час?',
        'Коля, как погода?',
        'Соня, как погода?',
        'Антон, как погода?'
    ]
    for query in queries:
        print(query, '-', process_query(query))


runner()

Результат:

Анфиса, сколько у меня друзей? - У тебя 10 друзей
Анфиса, кто все мои друзья? - Твои друзья: Сергей, Соня, Алексей, Миша, Дима, Алина, Егор, Коля, Артём, Петя
Анфиса, где все мои друзья? - Твои друзья в городах: Омск, Калининград, Михайловка, Красноярск, Москва, Челябинск, Владивосток, Пермь
Анфиса, кто виноват? - <неизвестный запрос>
Коля, ты где? - Коля в городе Красноярск
Соня, что делать? - <неизвестный запрос>
Антон, ты где? - У тебя нет друга по имени Антон
Алексей, который час? - Там сейчас 15:50
Артём, который час? - Там сейчас 23:50
Антон, который час? - У тебя нет друга по имени Антон
Петя, который час? - <не могу определить время в городе Михайловка>
Коля, как погода? - ⛅️ ?️-10°C ?️→7.2 m/s
Соня, как погода? - ☀️ ?️-3°C ?️↑4.2 m/s
Антон, как погода? - У тебя нет друга по имени Антон
import requests

cities = [
    'Омск',
    'Калининград',
    'Челябинск',
    'Владивосток',
    'Красноярск',
    'Москва',
    'Екатеринбург'
]

def make_url(city):
    # в URL задаём город, в котором узнаем погоду
    return f'http://wttr.in/{city}'

def make_parameters():
    params = {
        'format': 2,  # погода одной строкой
        'M': ''  # скорость ветра в "м/с"
    }
    return params

def what_weather(city):
    # Напишите тело этой функции.
    # Не изменяйте остальной код!
    try:
        request = requests.get(make_url(city), params=make_parameters())
   
        if request.status_code == 200: return request.text
        else: return '<ошибка на сервере погоды>'
    except requests.ConnectionError : return '<сетевая ошибка>'

print('Погода в городах:')
for city in cities:
    print(city, what_weather(city))

Задача 1

Напишите функцию what_weather() (англ. «какая погода?»), которую затем будете использовать в коде Анфисы:

  • Выполните HTTP-запрос, поместив вызов функции get() внутрь блока try.
  • Значения URL и параметров получите из функций make_url() (в неё нужно передать нужный город как аргумент city) и make_parameters().
  • При «выбрасывании» исключения типа requests.ConnectionError (от англ. «ошибка соединения») — функция what_weather() должна возвращать сообщение об ошибке '<сетевая ошибка>'.
  • Если код HTTP-ответа равен 200 (всё хорошо), верните из функции текст ответа. В противном случае функция должна вернуть строку '<ошибка на сервере погоды>'.

Код:

import requests

cities = [
    'Омск',
    'Калининград',
    'Челябинск',
    'Владивосток',
    'Красноярск',
    'Москва',
    'Екатеринбург'
]

def make_url(city):
    # в URL задаём город, в котором узнаем погоду
    return f'http://wttr.in/{city}'

def make_parameters():
    params = {
        'format': 2,  # погода одной строкой
        'M': ''  # скорость ветра в "м/с"
    }
    return params

def what_weather(city):
    # Напишите тело этой функции.
    # Не изменяйте остальной код!
    try:
        request = requests.get(make_url(city), params=make_parameters())
        #print(request.status_code)
        if request.status_code == 200: return request.text
        else: return '<ошибка на сервере погоды>'
    except requests.ConnectionError : return '<сетевая ошибка>'

print('Погода в городах:')
for city in cities:
    print(city, what_weather(city))

Результат:

Погода в городах:
Омск ? ?️-7°C ?️↖4.2 m/s

Калининград ⛅️ ?️+2°C ?️↖6.1 m/s

Челябинск ? ?️-3°C ?️↘6.1 m/s

Владивосток ☀️ ?️-3°C ?️↓7.2 m/s

Красноярск ⛅️ ?️-10°C ?️→8.3 m/s

Москва ☀️ ?️-2°C ?️↑4.2 m/s

Екатеринбург ? ?️-2°C ?️↘4.2 m/s

Задача 2

Это задание — финальное.

В нём вы сделаете Анфису мастером на все руки.

Анфиса будет знать всё про ваших друзей — где они, сколько у них времени, и какая у них погода.В список запросов queries в функции runner() добавлены новые запросы про погоду:

  • Коля, как погода?
  • Соня, как погода?
  • Антон, как погода?

Научите Анфису отвечать на вопросы такого вида.

Для этого:

  1. Добавьте в функцию process_friend() обработку ещё одного запроса 'как погода?'. Для получения ответа на этот вопрос используйте значение city — это город, в котором живёт друг.
  2. Затем вызовите функцию what_weather() — вы написали на прошлом уроке почти такую же. Она уже доступна в коде этого задания.
  3. Верните результат выполнения этой функции как результат process_friend().

Код:

import datetime as dt
import requests

DATABASE = {
    'Сергей': 'Омск',
    'Соня': 'Москва',
    'Алексей': 'Калининград',
    'Миша': 'Москва',
    'Дима': 'Челябинск',
    'Алина': 'Красноярск',
    'Егор': 'Пермь',
    'Коля': 'Красноярск',
    'Артём': 'Владивосток',
    'Петя': 'Михайловка'
}

UTC_OFFSET = {
    'Москва': 3,
    'Санкт-Петербург': 3,
    'Новосибирск': 7,
    'Екатеринбург': 5,
    'Нижний Новгород': 3,
    'Казань': 3,
    'Челябинск': 5,
    'Омск': 6,
    'Самара': 4,
    'Ростов-на-Дону': 3,
    'Уфа': 5,
    'Красноярск': 7,
    'Воронеж': 3,
    'Пермь': 5,
    'Волгоград': 4,
    'Краснодар': 3,
    'Калининград': 2,
    'Владивосток': 10
}


def format_count_friends(count_friends):
    if count_friends == 1:
        return '1 друг'
    elif 2 <= count_friends <= 4:
        return f'{count_friends} друга'
    else:
        return f'{count_friends} друзей'


def what_time(city):
    offset = UTC_OFFSET[city]
    city_time = dt.datetime.utcnow() + dt.timedelta(hours=offset)
    f_time = city_time.strftime("%H:%M")
    return f_time


def what_weather(city):
    url = f'http://wttr.in/{city}'
    weather_parameters = {
        'format': 2,
        'M': ''
    }
    try:
        response = requests.get(url, params=weather_parameters)
    except requests.ConnectionError:
        return '<сетевая ошибка>'
    if response.status_code == 200:
        return response.text.strip()
    else:
        return '<ошибка на сервере погоды>'


def process_anfisa(query):
    if query == 'сколько у меня друзей?':
        count_string = format_count_friends(len(DATABASE))
        return f'У тебя {count_string}'
    elif query == 'кто все мои друзья?':
        friends_string = ', '.join(DATABASE.keys())
        return f'Твои друзья: {friends_string}'
    elif query == 'где все мои друзья?':
        unique_cities = set(DATABASE.values())
        cities_string = ', '.join(unique_cities)
        return f'Твои друзья в городах: {cities_string}'
    else:
        return '<неизвестный запрос>'


def process_friend(name, query):
    if name in DATABASE:
        city = DATABASE[name]
        if query == 'ты где?':
            return f'{name} в городе {city}'
        elif query == 'который час?':
            if city not in UTC_OFFSET:
                return f'<не могу определить время в городе {city}>'
            time = what_time(city)
            return f'Там сейчас {time}'
        elif query == 'как погода?':
            return what_weather(city)
            
        else:
            return '<неизвестный запрос>'
    else:
        return f'У тебя нет друга по имени {name}'


def process_query(query):
    tokens = query.split(', ')
    name = tokens[0]
    if name == 'Анфиса':
        return process_anfisa(tokens[1])
    else:
        return process_friend(name, tokens[1])


def runner():
    queries = [
        'Анфиса, сколько у меня друзей?',
        'Анфиса, кто все мои друзья?',
        'Анфиса, где все мои друзья?',
        'Анфиса, кто виноват?',
        'Коля, ты где?',
        'Соня, что делать?',
        'Антон, ты где?',
        'Алексей, который час?',
        'Артём, который час?',
        'Антон, который час?',
        'Петя, который час?',
        'Коля, как погода?',
        'Соня, как погода?',
        'Антон, как погода?'
    ]
    for query in queries:
        print(query, '-', process_query(query))


runner()

Результат:

Анфиса, сколько у меня друзей? - У тебя 10 друзей
Анфиса, кто все мои друзья? - Твои друзья: Сергей, Соня, Алексей, Миша, Дима, Алина, Егор, Коля, Артём, Петя
Анфиса, где все мои друзья? - Твои друзья в городах: Омск, Калининград, Михайловка, Красноярск, Москва, Челябинск, Владивосток, Пермь
Анфиса, кто виноват? - <неизвестный запрос>
Коля, ты где? - Коля в городе Красноярск
Соня, что делать? - <неизвестный запрос>
Антон, ты где? - У тебя нет друга по имени Антон
Алексей, который час? - Там сейчас 15:50
Артём, который час? - Там сейчас 23:50
Антон, который час? - У тебя нет друга по имени Антон
Петя, который час? - <не могу определить время в городе Михайловка>
Коля, как погода? - ⛅️ ?️-10°C ?️→7.2 m/s
Соня, как погода? - ☀️ ?️-3°C ?️↑4.2 m/s
Антон, как погода? - У тебя нет друга по имени Антон

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

Напишем программу, которая будет считать обратные значения для целых чисел из заданного диапазона и выводить их в одну строку с разделителем «;». Один из вариантов кода для решения этой задачи выглядит так:

print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))

Программа получилась в одну строчку за счёт использования списочных выражений. Однако при вводе диапазона чисел, включающем в себя 0 (например, от -1 до 1), программа выдаст следующую ошибку:

ZeroDivisionError: division by zero

В программе произошла ошибка «деление на ноль». Такая ошибка, возникающая при выполнении программы и останавливающая её работу, называется исключением.

Попробуем в нашей программе избавиться от возникновения исключения деления на ноль. Пусть при попадании 0 в диапазон чисел, обработка не производится и выводится сообщение «Диапазон чисел содержит 0». Для этого нужно проверить до списочного выражения наличие нуля в диапазоне:

interval = range(int(input()), int(input()) + 1)
if 0 in interval:
    print("Диапазон чисел содержит 0.")
else:
    print(";".join(str(1 / x) for x in interval))

Теперь для диапазона, включающего в себя 0, например, от -2 до 2, исключения ZeroDivisionError не возникнет. Однако при вводе строки, которую невозможно преобразовать в целое число (например, «a»), будет вызвано другое исключение:

ValueError: invalid literal for int() with base 10: 'a'

Произошло исключение ValueError. Для борьбы с этой ошибкой нам придётся проверить, что строка состоит только из цифр. Сделать это нужно до преобразования в число. Тогда наша программа будет выглядеть так:

start = input()
end = input()
# Метод lstrip("-"), удаляющий символы "-" в начале строки, нужен для учёта
# отрицательных чисел, иначе isdigit() вернёт для них False
if not (start.lstrip("-").isdigit() and end.lstrip("-").isdigit()):
    print("Необходимо ввести два числа.")
else:
    interval = range(int(start), int(end) + 1)
    if 0 in interval:
        print("Диапазон чисел содержит 0.")
    else:
        print(";".join(str(1 / x) for x in interval))

Теперь наша программа работает без ошибок и при вводе строк, которые нельзя преобразовать в целое число.

Подход, который был нами применён для предотвращения ошибок, называется «Look Before You Leap» (LBYL), или «посмотри перед прыжком». В программе, реализующей такой подход, проверяются возможные условия возникновения ошибок до исполнения основного кода.

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

Существует другой подход для работы с ошибками: «Easier to Ask Forgiveness than Permission» (EAFP) или «проще извиниться, чем спрашивать разрешение». В этом подходе сначала исполняется код, а в случае возникновения ошибок происходит их обработка. Подход EAFP реализован в Python в виде обработки исключений.

Исключения в Python являются классами ошибок. В Python есть много стандартных исключений. Они имеют определённую иерархию за счёт механизма наследования классов. В документации Python версии 3.10.8 приводится следующее дерево иерархии стандартных исключений:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- EncodingWarning
           +-- ResourceWarning

Для обработки исключения в Python используется следующий синтаксис:

try:
    <код , который может вызвать исключения при выполнении>
except <классисключения_1>:
    <код обработки исключения>
except <классисключения_2>:
    <код обработки исключения>
...
else:
    <код выполняется, если не вызвано исключение в блоке try>
finally:
    <код , который выполняется всегда>

Блок try содержит код, в котором нужно обработать исключения, если они возникнут. При возникновении исключения интерпретатор последовательно проверяет в каком из блоков except обрабатывается это исключение. Исключение обрабатывается в первом блоке except, обрабатывающем класс этого исключения или базовый класс возникшего исключения. Необходимо учитывать иерархию исключений для определения порядка их обработки в блоках except. Начинать обработку исключений следует с более узких классов исключений. Если начать с более широкого класса исключения, например, Exception, то всегда при возникновении исключения будет срабатывать первый блок except. Сравните два следующих примера. В первом порядок обработки исключений указан от производных классов к базовым, а во втором – наоборот.

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")

При вводе значений «0» и «a» получим ожидаемый соответствующий возникающим исключениям вывод:

Невозможно преобразовать строку в число.

и

Ошибка деления на ноль.

Второй пример:

try:
    print(1 / int(input()))
except Exception:
    print("Неизвестная ошибка.")
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")

При вводе значений «0» и «a» получим в обоих случаях неинформативный вывод:

Неизвестная ошибка.

Необязательный блок else выполняет код в случае, если в блоке try не вызвано исключение. Добавим блок else в пример для вывода сообщения об успешном выполнении операции:

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")
else:
    print("Операция выполнена успешно.")

Теперь при вводе корректного значения, например, «5», вывод программы будет следующим:

2.0
Операция выполнена успешно.

Блок finally выполняется всегда, даже если возникло какое-то исключение, не учтённое в блоках except или код в этих блоках сам вызвал какое-либо исключение. Добавим в нашу программу вывод строки «Программа завершена» в конце программы даже при возникновении исключений:

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")
else:
    print("Операция выполнена успешно.")
finally:
    print("Программа завершена.")

Перепишем код, созданный с применением подхода LBYL, для первого примера из этой главы с использованием обработки исключений:

try:
    print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))
except ZeroDivisionError:
    print("Диапазон чисел содержит 0.")
except ValueError:
    print("Необходимо ввести два числа.")

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

Исключения можно принудительно вызывать с помощью оператора raise. Этот оператор имеет следующий синтаксис:

raise <класс исключения>(параметры)

В качестве параметра можно, например, передать строку с сообщением об ошибке.

В Python можно создавать свои собственные исключения. Синтаксис создания исключения такой же, как и у создания класса. При создании исключения его необходимо наследовать от какого-либо стандартного класса-исключения.

Напишем программу, которая выводит сумму списка целых чисел, и вызывает исключение, если в списке чисел есть хотя бы одно чётное или отрицательное число. Создадим свои классы исключений:

  • NumbersError – базовый класс исключения;
  • EvenError – исключение, которое вызывается при наличии хотя бы одного чётного числа;
  • NegativeError – исключение, которое вызывается при наличии хотя бы одного отрицательного числа.
class NumbersError(Exception):
    pass


class EvenError(NumbersError):
    pass


class NegativeError(NumbersError):
    pass


def no_even(numbers):
    if all(x % 2 != 0 for x in numbers):
        return True
    raise EvenError("В списке не должно быть чётных чисел")


def no_negative(numbers):
    if all(x >= 0 for x in numbers):
        return True
    raise NegativeError("В списке не должно быть отрицательных чисел")


def main():
    print("Введите числа в одну строку через пробел:")
    try:
        numbers = [int(x) for x in input().split()]
        if no_negative(numbers) and no_even(numbers):
            print(f"Сумма чисел равна: {sum(numbers)}.")
    except NumbersError as e:  # обращение к исключению как к объекту
        print(f"Произошла ошибка: {e}.")
    except Exception as e:
        print(f"Произошла непредвиденная ошибка: {e}.")

        
if __name__ == "__main__":
    main()

Обратите внимание: в программе основной код выделен в функцию main. А код вне функций содержит только условный оператор и вызов функции main при выполнении условия __name__ == "__main__". Это условие проверяет, запущен ли файл как самостоятельная программа или импортирован как модуль.

Любая программа, написанная на языке программирования Python может быть импортирована как модуль в другую программу. В идеологии Python импортировать модуль – значит полностью его выполнить. Если основной код модуля содержит вызовы функций, ввод или вывод данных без использования указанного условия __name__ == "__main__", то произойдёт полноценный запуск программы. А это не всегда удобно, если из модуля нужна только отдельная функция или какой-либо класс.

При изучении модуля itertools, мы говорили о том, как импортировать модуль в программу. Покажем ещё раз два способа импорта на примере собственного модуля.

Для импорта модуля из файла, например example_module.py, нужно указать его имя, если он находится в той же папке, что и импортирующая его программа:

import example_module

Если требуется отдельный компонент модуля, например функция или класс, то импорт можно осуществить так:

from example_module import some_function, ExampleClass

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

Предисловие

Недавно для себя открыл сервис Яндекса — Практикум. Мне очень понравилась идея, и я прошёл серию бесплатных уроков курса Python beckend-разработчик. Бесплатный курс называется «Основы Python»

Хоть я и не новичок в Python-е, но курс для меня оказался очень полезным. Он учит не только основам программированию, но и правильному подходу к работе для новичков.

У меня отец хотел научиться. Ему, между прочим, 75 лет. Он попробовал, но у него не получилось. Камнем преткновения оказалось развернуть и пользоваться у себя на компьютере какую-нибудь IDE. Он начал изучать по книжке, а в книжке ничего про это не было.

Ему просто хотелось попрактиковаться программированию, понять вообще что это такое, приобщиться к новому опыту. И самое главное, просто понять, что это за программирование такое. Я разбираться в новом и непонятном редакторе IDE ему не хотелось.

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

На данный момент, отец прошёл несколько уроков и втянулся. Посмотрим, чем это всё закончится.

Что я хочу сделать

Курс Яндекса устроен таким образом, что тебя как будто берут стажёром в IT-компанию. Там тебе дают в разработку небольшой проект, и ты постепенно его разрабатываешь начиная с самых азов и довольно глубоко.

Всё это происходит онлайн и соответственно весь этот проект остаётся на серверах Яндекса. А мне хочется получить этот проект, сначала, локально, затем, с ним поэкспериментировать и, наконец, выложить на Github-е.

Установка Python, Django и IDE PyCharm

У меня стоит совершенно новая система UBUNTU MATE 20.04, и все установки и настройки буду описывать именно для неё.

Установка Python

На самом деле в Ubuntu 20.04 уже стоит Python 3.8.2 и его устанавливать не надо. Но в будущем потребуется устанавливать различные модули, а для этого нужен менеджер пакетов pip.

sudo apt-get install python3-pip

Указанная выше команда установит пакетный менеджер и Python теперь полностью готов для работы.

Установка Django

Проект курса Яндекса использует Framework Django, а значит установить его нужно обязательно.

Вообще, насколько мне известно, установить Django можно тремя разными способами: в проект, текущему пользователю и в систему.

При установке локальному пользователю, консольные команды Django не попадают в пути по умолчанию $PATH, и вызывать их приходиться прописывая полный путь: ‘~/.local/bin/django-admin.

Часто вызывать такую команду не очень удобно, поэтому я решил установить Django в систему:

sudo python3 -m pip install django --system

Установка PyCharm

Конечно PyCharm не единственная IDE для разработки на Python, но я к ней уже привык.

Установить бесплатную версию PyCharm можно командой:

sudo snap install pycharm-community --classic

При первом запуске PyCharm можно выбрать светлую или тёмную тему. Я выбираю тёмную тему.

Резюме

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

sudo apt-get install python3-pip # Установка # менеджера пакетов Питона sudo python3 -m pip install django --system # Установка Django sudo snap install pycharm-community --classic # Установка # PyCharm

Знакомство с Django

Знакомство с Django на курсе Яндекс начинается уже с подготовленного проекта.

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

Однако, мне нужно, чтобы всё заработало на моём локальном компьютере. А для этого нужно создать проект. Проект будет называться «Anfisa for Friends». Это аналог Алисы Яндекса.

Создание Python-проекта в PyCharm

Сперва наперво, мы создаём пустой проект со следующими установками:

Настройки проекта PyCharm

Создание Django-проекта

Теперь заходим в директорию нашего проекта и создаем каркас приложения на Django:

django-admin startproject anfisa4friends

В результате получаем структуру нового проекта:

Структура Django-проекта

Структура получилась не совсем такая, как в курсе Яндекса.

Во-первых, нужно было запустить ‘django-admin’ не в папке проекта, а в папке проектов. Я это переделаю.

Запуск Django-проекта

Остальными переделками я займусь позже, а сейчас нужно проверить работает ли Django. Для этого в папке проекта запускаем:

python3 manage.py runserver

Теперь нужно перейти в браузер и набрать в строке адреса: http://127.0.0.1:8000/.

Если всё работает, то в окне браузера должно появиться следующее:

Успешный запуск Django

Ура! Ура! Ура-а-а-а! Всё работает!

Резюме

Как выяснилось для работы с Django нет необходимости сначала создавать Python-проект.

Для знакомства с Django мы используем две команды:

django-admin startproject anfisa4friends # создание проекта python3 manage.py runserver # запуск проекта

Проверить работу сайта проекта можно по адресу: http://127.0.0.1:8000/

Все ошибки или предупреждения можно прочитать в терминале.

Следующая команда обновляет базу данных и все предупреждения исчезают.

python3 manage.py migrate # Обновление базы данных проекта

И так, у нас есть каркас Django-проекта, который работает, и что-то там печатает в окне браузера.

Знакомство с Git

Однако, нам хотелось бы, чтобы проект печатал не абы какой текст, а наш текст. К тому же в терминале, при запуске проекта, выскакивает предупреждение, суть которого в том, что проект может работать неправильно, до запуска python3 manage.py migrate.

Чтобы всё это исправить, нужно внести изменения. Самый простой способ это сделать — это удалить лишние файлы, а содержимой оставшихся файлов заменить содержимым файлов из курса Яндекс практикума.

Однако, в этом случае мы не поймём, какие изменения были сделаны и нечему не научимся. Нам необходимо сравнить файлы в нашем проекте с файлами в проекте Яндекса, а ключевые изменения понять и запомнить.

Для контроля всех изменений в проекте, и особенно изменений в файле с одинаковым названием, существует система контроля версий Git.

Установка Git

В моей версии Ubuntu Git не установлен по умолчанию. Установить Git можно следующей командой:

sudo apt install git # Установка утилиты Git

Создание git-репозитория

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

git init # Создания git-репозитория

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

git status

Если репозиторий успешно создан, мы увидим следующий вывод в терминале:

Команда git status

Сообщение «Ещё нет коммитов» говорит о том, что в репозитории ещё ничего не сохранено. А красным выделены файлы и папки, которые могут быть сохранены.

Настройка git-репозитория

Для правильной работы git-репозитория требуются учётные данные. Они задаются следующими командами.

git config --global user.email "deviur@yandex.ru" git config --global user.name "Deviur"

Добавляем файл .gitignore

Если теперь запустить проект, то в проекте автоматически будут созданы две папки.

Какие файлы исключить с .gitignore

Эти папки нам не нужно сохранять в проект.

Для того чтобы исключить папки и файлы из проекта используется файл ‘.gitignore’. В этот файл записывается всё, что не нужно включать в проект.

.idea/ anfisa4friends/__pycache__/

Сохранение текущего состояния проекта

Прежде всего нам нужно выбрать какие файлы и папки добавить в репозиторий.

Чтобы добавить все доступные файлы используется следующая команда:

git add . # добавить файлы для сохранения в git-репозиторий

Теперь вывод `git status` показывает все изменения, которые произошли с файлами, которые мы добавили.

Результат команды git status

Как видно, коммитов ещё нет. Это значит текущее состояние мы ещё не сохранили. Мы только отметили, какие изменения и какие файлы сохранить.

Теперь сохраняем текущее состояние проекта в репозиторий:

git commit -m'Start Django-project'

Ура! Проект сохранён в git-репозитории. Теперь он имеет следующую структуру.

k

Резюме

Для начала работы с Git и сохранения нашего проекта мы использовали следующие команды:

sudo apt install git # установка Git git init # создание git-репозитория git status # проверка состояние git-репозитория # Настройка учётных данных git-репозитория: git config --global user.email "deviur@yandex.ru" git config --global user.name "Deviur" git add . # добавить файлы для сохранения в git-репозиторий git commit -m'Start Django-project' # сохранить проект # в репозиторий

Пишем проект anfisa4friends

Минимальные изменения

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

Ниже красным обозначено, то что нужно удалить, а синим то, что нужно вставить.

  1. Удаляем все файлы, которых не должно быть в проекте.
  2. В файле ‘settings.py‘ удаляем строку:
    WSGI_APPLICATION = 'anfisa4friends.wsgi.application'
    (остальное можно не менять).
  3. В файле ‘urls.py‘ после строки
    from django.contrib import admin
    вставляем
    from homepage import views as home_views,
    а также, ниже изменяем список urlpatterns на следующий:
    urlpatterns = [
    path('', home_views.index),
    ]
  4. Создаём папку ‘homepage‘.
  5. Создаём в этой папке файл ‘views.py’ со следующим кодом:
    from django.http import HttpResponse
    def index(request):
    return HttpResponse('Anfisa for Friends -- Hello, world! -- ¯(°:°)/¯')

Теперь запускаем проект и смотрим. Всё должно работать!

Что я не стал менять

У нас получилось три версии проекта anfisa4friends:

  1. Версия сохранённая на Яндекс-практикуме.
  2. Версия созданная Django, которую мы сохранили в git-репозитории.
  3. И последняя версия, с изменениями, которые мы сделали в предыдущем разделе.

Часть кода я умышленно не стал делать в точности как на курсе Яндекс-практикума. Код созданный Django мне показался более изящным. Посудите сами:

# Код созданный Django BASE_DIR = Path(__file__).resolve().parent.parent # Код с курса Яндекс-практикума BASE_DIR = os.path.dirname( os.path.dirname(os.path.abspath(__file__)) )

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

На самом деле, проект Яндекс-практикума не использует базы данных, и не совсем понятно, зачем вообще этот кусок кода в проекте. Я не стал ничего удалять, но закомментировал.

Знакомство с Github.com

Структура git-репозитория

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

  1. tasks (задания) — здесь будет код, который дан, как условие в Яндекс-практикуме.
  2. solutions (решения) — здесь мой код, который я предложил в качестве решения и Яндекс его принял, как правильный.
  3. master — основная ветка, в которой вы можете экспериментировать или пройти курс самостоятельно.

Создание структуры веток git-репозитория

У меня Яндекс-практикум уже пройден и сохранены только решения, поэтому начну с ветки solutions.

Для создания ветки и переключения на неё используется следующая команда:

git branch solutions # создание ветки solutions git checkout # переключение на ветку solutions

Теперь сохраню в это ветку последовательно решения всех уроков Яндекс-практикума. В первый коммит будет сохранено моё решение на задачу из темы (topic) №9 урока (lesson) №4.

git commit -m'Solution: topic 9 lesson 4'

Ошибка проверки CSRF. Запрос отклонён.

При прохождении темы 11 на уроке 4 «POST-запросы» на Яндекс-практикуме стала выскакивать эта ошибка. В среде Яндекс-практикума всё работает, а на локальном компьютере, когда нажимаешь кнопку «Угостить» вылетает эта ошибка.

Пришлось покопаться в документации. В результате я внёс небольшие изменения в шаблон ‘index.html

Строку
<form action="/" method="post">,
я заменил на
<form action="/" method="post">{% csrf_token %}

Теперь всё заработало!

Выкладываем наш проект на Github

Для того, чтобы выложить проект на GitHub, у вас должен быть зарегистрированный аккаунт на github.com. У меня аккаунт зарегистрирован под именем «deviur«

Затем вы должны в интерфейсе GitHub создать свой проект. В нашем случае проект будет называться «anfisa4friends«

Далее на нашем локальном компьютере, нужно добавить в git-репозиторий адрес удалённого репозитория.

# Добавляем удалённый репозиторий git remote add origin https://github.com/deviur/anfisa4friends.git # Здесь deviur - моё имя пользователя на GitHub. Не забудьте заменить на своё.А anfisa4friends - это название проекта, который был создан на GitHub.

Теперь остаётся только отправить наш проект на GitHub. Для этого используются следующие команды:

# Отправляем на удалённый репозиторий ветку master git push -u origin master # Отправляем на удалённый репозиторий ветку solutions git push origin solutions

Вот и готово! Теперь наш проект на GitHub-е. Теперь его всегда можно восстановить у себя на компьютере с помощью команды:

git clone https://github.com/deviur/anfisa4friends.git

А ознакомиться с проектом можно по адресу: https://github.com/deviur/anfisa4friends


Задавайте вопросы, пишите пожелания и замечания в комментариях.

Нужно написать код по заданию, вроде как всё сделал но выдает ошибку «код работает, но сделал что то лишнее или что то не так»
Задание вот:

Напишите функцию what_weather() (англ. «какая погода?»), которую затем будете использовать в коде Анфисы:

  1. Выполните HTTP-запрос, поместив вызов функции get() внутрь блока try.
  2. Значения URL и параметров получите из функций make_url() (в неё нужно передать нужный город как аргумент city) и make_parameters().
  3. При «выбрасывании» исключения типа requests.ConnectionError (от англ. «ошибка соединения») — функция what_weather() должна возвращать сообщение об ошибке ‘<сетевая ошибка>’.
  4. Если код HTTP-ответа равен 200 (всё хорошо), верните из функции текст ответа. В противном случае функция должна вернуть строку ‘<ошибка на сервере погоды>’.

Скрин ошибки: https://pastenow.ru/7486809761d967f85c31e2849cc5160c

Сам код вот:

import requests


cities = [
    'Омск',
    'Калининград',
    'Челябинск',
    'Владивосток',
    'Красноярск',
    'Москва',
    'Екатеринбург'
]


def make_url(city):
    # в URL задаём город, в котором узнаем погоду
    return f'http://wttr.in/{city}'


def make_parameters():
    params = {
        'format': 2,  # погода одной строкой
        'M': ''  # скорость ветра в "м/с"
    }
    return params


def what_weather(city):
    try:
        response = requests.get(make_url(city), params=make_parameters())
        if response.status_code == 200:
            return response
        elif response.status_code != 200:
            return '<ошибка на сервере погоды>'
    except requests.ConnectionError:
        return '<сетевая ошибка>'



print('Погода в городах:')
for city in cities:
    print(city, what_weather(city))

задан 7 окт 2019 в 14:37

Никита Котов's user avatar

7

import requests

cities = [
    'Омск',
    'Калининград',
    'Челябинск',
    'Владивосток',
    'Красноярск',
    'Москва',
    'Екатеринбург'
]

def make_url(city):
    # в URL задаём город, в котором узнаем погоду
    return f'http://wttr.in/{city}'

def make_parameters():
    params = '0TMq&lang=ru'
    return params

def what_weather(city):
    try:
        r = requests.get(make_url(city), params=make_parameters())
        r.encoding = 'utf-8'   
        if r.status_code == 200:
            return r.text
        else:
            return '<ошибка на сервере погоды>'
    except requests.ConnectionError:
        return '<сетевая ошибка>'


print('Погода в городах:')
for city in cities:
    print(what_weather(city))

Список доступных параметров сервера погоды можно посмотреть здесь: https://wttr.in/:help

insolor's user avatar

insolor

45.9k16 золотых знаков54 серебряных знака95 бронзовых знаков

ответ дан 8 окт 2019 в 12:48

anshap's user avatar

anshapanshap

5353 серебряных знака6 бронзовых знаков

Обработка исключений

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

Напишем программу, которая будет считать обратные значения для целых чисел из заданного диапазона и выводить их в одну строку с разделителем ‘;’. Один из вариантов кода для решения этой задачи выглядит так:

print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))

Программа получилась в одну строчку за счёт использования списочных выражений. Однако при вводе диапазона чисел, включающего в себя 0 (например, от -1 до 1), программа выдаст следующую ошибку:

ZeroDivisionError: division by zero

В программе произошла ошибка «деление на ноль». Такая ошибка, возникающая при выполнении программы и останавливающая её работу, называется исключением.

Попробуем в нашей программе избавиться от возникновения исключения деления на ноль. Пусть при попадании 0 в диапазон чисел обработка не производится и выводится сообщение «Диапазон чисел содержит 0». Для этого нужно проверить до списочного выражения наличие нуля в диапазоне:

interval = range(int(input()), int(input()) + 1)
if 0 in interval:
    print("Диапазон чисел содержит 0.")
else:
    print(";".join(str(1 / x) for x in interval))

Теперь для диапазона, включающего в себя 0, например от -2 до 2, исключения ZeroDivisionError не возникнет. Однако при вводе строки, которую невозможно преобразовать в целое число (например, «a»), будет вызвано другое исключение:

ValueError: invalid literal for int() with base 10: 'a'

Произошло исключение ValueError. Для борьбы с этой ошибкой нам придётся проверить, что строка состоит только из цифр. Сделать это нужно до преобразования в число. Тогда наша программа будет выглядеть так:

start = input()
end = input()
# Метод lstrip("-"), удаляющий символы "-" в начале строки, нужен для учёта
# отрицательных чисел, иначе isdigit() вернёт для них False
if not (start.lstrip("-").isdigit() and end.lstrip("-").isdigit()):
    print("
    ввести два числа.")
else:
    interval = range(int(start), int(end) + 1)
    if 0 in interval:
        print("Диапазон чисел содержит 0.")
    else:
        print(";".join(str(1 / x) for x in interval))

Теперь наша программа работает без ошибок и при вводе строк, которые нельзя преобразовать в целое число.

Подход, который был нами применён для предотвращения ошибок, называется Look Before You Leap (LBYL), или «Посмотри перед прыжком». В программе, реализующей такой подход, проверяются возможные условия возникновения ошибок до исполнения основного кода.

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

Существует другой подход для работы с ошибками: Easier to Ask Forgiveness than Permission (EAFP), или «Проще попросить прощения, чем разрешения». В этом подходе сначала исполняется код, а в случае возникновения ошибок происходит их обработка. Подход EAFP реализован в Python в виде обработки исключений.

Исключения в Python являются классами ошибок. В Python есть много стандартных исключений. Они имеют определённую иерархию за счёт механизма наследования классов. В документации Python версии 3.10.8 приводится следующее дерево иерархии стандартных исключений:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- EncodingWarning
           +-- ResourceWarning

Для обработки исключения в Python используется следующий синтаксис:

try:
    <код , который может вызвать исключения при выполнении>
except <классисключения_1>:
    <код обработки исключения>
except <классисключения_2>:
    <код обработки исключения>
...
else:
    <код выполняется, если не вызвано исключение в блоке try>
finally:
    <код , который выполняется всегда>

Блок try содержит код, в котором нужно обработать исключения, если они возникнут.
При возникновении исключения интерпретатор последовательно проверяет, в каком из блоков except обрабатывается это исключение.
Исключение обрабатывается в первом блоке except, обрабатывающем класс этого исключения или базовый класс возникшего исключения.
Необходимо учитывать иерархию исключений для определения порядка их обработки в блоках except. Начинать обработку исключений следует с более узких классов исключений. Если начать с более широкого класса исключения, например Exception, то всегда при возникновении исключения будет срабатывать первый блок except.
Сравните два следующих примера. В первом порядок обработки исключений указан от производных классов к базовым, а во втором — наоборот.

Первый пример:

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")

При вводе значений «0» и «a» получим ожидаемый, соответствующий возникающим исключениям вывод:

Невозможно преобразовать строку в число.

и

Ошибка деления на ноль.

Второй пример:

try:
    print(1 / int(input()))
except Exception:
    print("Неизвестная ошибка.")
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")

При вводе значений «0» и «a» получим в обоих случаях неинформативный вывод:

Неизвестная ошибка.

Необязательный блок else выполняет код в случае, если в блоке try не вызвано исключение. Добавим блок else в пример для вывода сообщения об успешном выполнении операции:

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")
else:
    print("Операция выполнена успешно.")

Теперь при вводе корректного значения, например «5», вывод программы будет следующим:

2.0
Операция выполнена успешно.

Блок finally выполняется всегда, даже если возникло какое-то исключение, не учтённое в блоках except, или код в этих блоках сам вызвал какое-либо исключение. Добавим в нашу программу вывод строки «Программа завершена» в конце программы даже при возникновении исключений:

try:
    print(1 / int(input()))
except ZeroDivisionError:
    print("Ошибка деления на ноль.")
except ValueError:
    print("Невозможно преобразовать строку в число.")
except Exception:
    print("Неизвестная ошибка.")
else:
    print("Операция выполнена успешно.")
finally:
    print("Программа завершена.")

Перепишем код, созданный с применением подхода LBYL, для первого примера из этой главы с использованием обработки исключений:

try:
    print(";".join(str(1 / x) for x in range(int(input()), int(input()) + 1)))
except ZeroDivisionError:
    print("Диапазон чисел содержит 0.")
except ValueError:
    print("Необходимо ввести два числа.")

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

Исключения можно принудительно вызывать с помощью оператора raise. Этот оператор имеет следующий синтаксис:

raise <класс исключения>(параметры)

В качестве параметра можно, например, передать строку с сообщением об ошибке.

Создание собственных исключений

В Python можно создавать свои собственные исключения. Синтаксис создания исключения такой же, как и у создания класса. При создании исключения его необходимо наследовать от какого-либо стандартного класса-исключения.

Напишем программу, которая выводит сумму списка целых чисел и вызывает исключение, если в списке чисел есть хотя бы одно чётное или отрицательное число. Создадим свои классы исключений:

  • NumbersError — базовый класс исключения;
  • EvenError — исключение, которое вызывается при наличии хотя бы одного чётного числа;
  • NegativeError — исключение, которое вызывается при наличии хотя бы одного отрицательного числа.
class NumbersError(Exception):
    pass


class EvenError(NumbersError):
    pass


class NegativeError(NumbersError):
    pass


def no_even(numbers):
    if all(x % 2 != 0 for x in numbers):
        return True
    raise EvenError("В списке не должно быть чётных чисел")


def no_negative(numbers):
    if all(x >= 0 for x in numbers):
        return True
    raise NegativeError("В списке не должно быть отрицательных чисел")


def main():
    print("Введите числа в одну строку через пробел:")
    try:
        numbers = [int(x) for x in input().split()]
        if no_negative(numbers) and no_even(numbers):
            print(f"Сумма чисел равна: {sum(numbers)}.")
    except NumbersError as e:  # обращение к исключению как к объекту
        print(f"Произошла ошибка: {e}.")
    except Exception as e:
        print(f"Произошла непредвиденная ошибка: {e}.")

        
if __name__ == "__main__":
    main()

Модули

Обратите внимание: в программе основной код выделен в функцию main. А код вне функций содержит только условный оператор и вызов функции main при выполнении условия __name__ == "__main__". Это условие проверяет, запущен ли файл как самостоятельная программа или импортирован как модуль.

Любая программа, написанная на языке программирования Python, может быть импортирована как модуль в другую программу. В идеологии Python импортировать модуль — значит полностью его выполнить. Если основной код модуля содержит вызовы функций, ввод или вывод данных без использования указанного условия __name__ == "__main__", то произойдёт полноценный запуск программы. А это не всегда удобно, если из модуля нужна только отдельная функция или какой-либо класс.

При изучении модуля itertools мы говорили о том, как импортировать модуль в программу. Покажем ещё раз два способа импорта на примере собственного модуля.

Для импорта модуля из файла, например example_module.py, нужно указать его имя, если он находится в той же папке, что и импортирующая его программа:

import example_module

Если требуется отдельный компонент модуля, например функция или класс, то импорт можно осуществить так:

from example_module import some_function, ExampleClass

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

Рассмотрим написанное выше на примере. Пусть имеется программа module_hello.py, в которой находится функция hello(name), возвращающая строку приветствия пользователя по имени. В самой программе кроме функции присутствует вызов этой функции и печать результата её работы. Импортируем из модуля module_hello.py функцию hello(name) в другую программу program.py и также используем для вывода приветствия пользователя.

Код программы module_hello.py:

def hello(name):
    return f"Привет, {name}!"


print(hello(input("Введите своё имя: ")))

Код программы program.py:

from module_hello import hello

print(hello(input("Добрый день. Введите имя: ")))

При выполнении program.py нас ожидает неожиданное действие. Программа сначала запросит имя пользователя, а затем сделает это ещё раз, но с приветствием из program.py.

Введите своё имя: Андрей
Привет, Андрей!
Добрый день. Введите имя: Андрей
Привет, Андрей!

Наша ошибка заключается в том, что программа module_hello.py выполняется полностью, включая основной код с вызовом функции и выводом результата. Исправим программу module_hello.py, добавив проверку, запущена программа или импортирована как модуль:

def hello(name):
    return f"Привет, {name}!"


if __name__ == "__main__":
    print(hello(input("Введите своё имя: ")))

Теперь при импорте модуля module_hello.py код в теле условного оператора выполняться не будет. А основной код этой программы выполнится только при запуске файла как отдельной программы.
Для большего удобства обычно в теле указанного условного оператора вызывают функцию main(), а основной код программы оформляют уже внутри этой функции.
Тогда наш модуль можно переписать так:

def hello(name):
    return f"Привет, {name}!"


def main():
    print(hello(input("Введите своё имя: ")))


if __name__ == "__main__":
    main()

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

from some_module import *

Однако делать так крайне не рекомендуется, потому что все объекты модуля добавляются в пространство имён нашей программы, что может приводить к конфликтам.

Понравилась статья? Поделить с друзьями:
  • Обработка ошибки 404 spring
  • Обработка ошибок python selenium
  • Обработка ошибки 404 django
  • Обработка ошибок python raise
  • Обработка ошибки 403