Обработка ошибок увеличивает отказоустойчивость кода, защищая его от потенциальных сбоев, которые могут привести к преждевременному завершению работы.
Прежде чем переходить к обсуждению того, почему обработка исключений так важна, и рассматривать встроенные в Python исключения, важно понять, что есть тонкая грань между понятиями ошибки и исключения.
Ошибку нельзя обработать, а исключения Python обрабатываются при выполнении программы. Ошибка может быть синтаксической, но существует и много видов исключений, которые возникают при выполнении и не останавливают программу сразу же. Ошибка может указывать на критические проблемы, которые приложение и не должно перехватывать, а исключения — состояния, которые стоит попробовать перехватить. Ошибки — вид непроверяемых и невозвратимых ошибок, таких как OutOfMemoryError
, которые не стоит пытаться обработать.
Обработка исключений делает код более отказоустойчивым и помогает предотвращать потенциальные проблемы, которые могут привести к преждевременной остановке выполнения. Представьте код, который готов к развертыванию, но все равно прекращает работу из-за исключения. Клиент такой не примет, поэтому стоит заранее обработать конкретные исключения, чтобы избежать неразберихи.
Ошибки могут быть разных видов:
- Синтаксические
- Недостаточно памяти
- Ошибки рекурсии
- Исключения
Разберем их по очереди.
Синтаксические ошибки (SyntaxError)
Синтаксические ошибки часто называют ошибками разбора. Они возникают, когда интерпретатор обнаруживает синтаксическую проблему в коде.
Рассмотрим на примере.
a = 8
b = 10
c = a b
File "", line 3
c = a b
^
SyntaxError: invalid syntax
Стрелка вверху указывает на место, где интерпретатор получил ошибку при попытке исполнения. Знак перед стрелкой указывает на причину проблемы. Для устранения таких фундаментальных ошибок Python будет делать большую часть работы за программиста, выводя название файла и номер строки, где была обнаружена ошибка.
Недостаточно памяти (OutofMemoryError)
Ошибки памяти чаще всего связаны с оперативной памятью компьютера и относятся к структуре данных под названием “Куча” (heap
). Если есть крупные объекты (или) ссылки на подобные, то с большой долей вероятности возникнет ошибка OutofMemory
. Она может появиться по нескольким причинам:
- Использование 32-битной архитектуры Python (максимальный объем выделенной памяти невысокий, между 2 и 4 ГБ);
- Загрузка файла большого размера;
- Запуск модели машинного обучения/глубокого обучения и много другое;
Обработать ошибку памяти можно с помощью обработки исключений — резервного исключения. Оно используется, когда у интерпретатора заканчивается память и он должен немедленно остановить текущее исполнение. В редких случаях Python вызывает OutofMemoryError
, позволяя скрипту каким-то образом перехватить самого себя, остановить ошибку памяти и восстановиться.
Но поскольку Python использует архитектуру управления памятью из языка C (функция malloc()
), не факт, что все процессы восстановятся — в некоторых случаях MemoryError
приведет к остановке. Следовательно, обрабатывать такие ошибки не рекомендуется, и это не считается хорошей практикой.
Ошибка рекурсии (RecursionError)
Эта ошибка связана со стеком и происходит при вызове функций. Как и предполагает название, ошибка рекурсии возникает, когда внутри друг друга исполняется много методов (один из которых — с бесконечной рекурсией), но это ограничено размером стека.
Все локальные переменные и методы размещаются в стеке. Для каждого вызова метода создается стековый кадр (фрейм), внутрь которого помещаются данные переменной или результат вызова метода. Когда исполнение метода завершается, его элемент удаляется.
Чтобы воспроизвести эту ошибку, определим функцию recursion
, которая будет рекурсивной — вызывать сама себя в бесконечном цикле. В результате появится ошибка StackOverflow
или ошибка рекурсии, потому что стековый кадр будет заполняться данными метода из каждого вызова, но они не будут освобождаться.
def recursion():
return recursion()
recursion()
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
in
----> 1 recursion()
in recursion()
1 def recursion():
----> 2 return recursion()
... last 1 frames repeated, from the frame below ...
in recursion()
1 def recursion():
----> 2 return recursion()
RecursionError: maximum recursion depth exceeded
Ошибка отступа (IndentationError)
Эта ошибка похожа по духу на синтаксическую и является ее подвидом. Тем не менее она возникает только в случае проблем с отступами.
Пример:
for i in range(10):
print('Привет Мир!')
File "", line 2
print('Привет Мир!')
^
IndentationError: expected an indented block
Исключения
Даже если синтаксис в инструкции или само выражение верны, они все равно могут вызывать ошибки при исполнении. Исключения Python — это ошибки, обнаруживаемые при исполнении, но не являющиеся критическими. Скоро вы узнаете, как справляться с ними в программах Python. Объект исключения создается при вызове исключения Python. Если скрипт не обрабатывает исключение явно, программа будет остановлена принудительно.
Программы обычно не обрабатывают исключения, что приводит к подобным сообщениям об ошибке:
Ошибка типа (TypeError)
a = 2
b = 'PythonRu'
a + b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in
1 a = 2
2 b = 'PythonRu'
----> 3 a + b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Ошибка деления на ноль (ZeroDivisionError)
10 / 0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in
----> 1 10 / 0
ZeroDivisionError: division by zero
Есть разные типы исключений в Python и их тип выводится в сообщении: вверху примеры TypeError
и ZeroDivisionError
. Обе строки в сообщениях об ошибке представляют собой имена встроенных исключений Python.
Оставшаяся часть строки с ошибкой предлагает подробности о причине ошибки на основе ее типа.
Теперь рассмотрим встроенные исключения Python.
Встроенные исключения
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
+-- ResourceWarning
Прежде чем переходить к разбору встроенных исключений быстро вспомним 4 основных компонента обработки исключения, как показано на этой схеме.
Try
: он запускает блок кода, в котором ожидается ошибка.Except
: здесь определяется тип исключения, который ожидается в блокеtry
(встроенный или созданный).Else
: если исключений нет, тогда исполняется этот блок (его можно воспринимать как средство для запуска кода в том случае, если ожидается, что часть кода приведет к исключению).Finally
: вне зависимости от того, будет ли исключение или нет, этот блок кода исполняется всегда.
В следующем разделе руководства больше узнаете об общих типах исключений и научитесь обрабатывать их с помощью инструмента обработки исключения.
Ошибка прерывания с клавиатуры (KeyboardInterrupt)
Исключение KeyboardInterrupt
вызывается при попытке остановить программу с помощью сочетания Ctrl + C
или Ctrl + Z
в командной строке или ядре в Jupyter Notebook. Иногда это происходит неумышленно и подобная обработка поможет избежать подобных ситуаций.
В примере ниже если запустить ячейку и прервать ядро, программа вызовет исключение KeyboardInterrupt
. Теперь обработаем исключение KeyboardInterrupt
.
try:
inp = input()
print('Нажмите Ctrl+C и прервите Kernel:')
except KeyboardInterrupt:
print('Исключение KeyboardInterrupt')
else:
print('Исключений не произошло')
Исключение KeyboardInterrupt
Стандартные ошибки (StandardError)
Рассмотрим некоторые базовые ошибки в программировании.
Арифметические ошибки (ArithmeticError)
- Ошибка деления на ноль (Zero Division);
- Ошибка переполнения (OverFlow);
- Ошибка плавающей точки (Floating Point);
Все перечисленные выше исключения относятся к классу Arithmetic
и вызываются при ошибках в арифметических операциях.
Деление на ноль (ZeroDivisionError)
Когда делитель (второй аргумент операции деления) или знаменатель равны нулю, тогда результатом будет ошибка деления на ноль.
try:
a = 100 / 0
print(a)
except ZeroDivisionError:
print("Исключение ZeroDivisionError." )
else:
print("Успех, нет ошибок!")
Исключение ZeroDivisionError.
Переполнение (OverflowError)
Ошибка переполнение вызывается, когда результат операции выходил за пределы диапазона. Она характерна для целых чисел вне диапазона.
try:
import math
print(math.exp(1000))
except OverflowError:
print("Исключение OverFlow.")
else:
print("Успех, нет ошибок!")
Исключение OverFlow.
Ошибка утверждения (AssertionError)
Когда инструкция утверждения не верна, вызывается ошибка утверждения.
Рассмотрим пример. Предположим, есть две переменные: a
и b
. Их нужно сравнить. Чтобы проверить, равны ли они, необходимо использовать ключевое слово assert
, что приведет к вызову исключения Assertion
в том случае, если выражение будет ложным.
try:
a = 100
b = "PythonRu"
assert a == b
except AssertionError:
print("Исключение AssertionError.")
else:
print("Успех, нет ошибок!")
Исключение AssertionError.
Ошибка атрибута (AttributeError)
При попытке сослаться на несуществующий атрибут программа вернет ошибку атрибута. В следующем примере можно увидеть, что у объекта класса Attributes
нет атрибута с именем attribute
.
class Attributes(obj):
a = 2
print(a)
try:
obj = Attributes()
print(obj.attribute)
except AttributeError:
print("Исключение AttributeError.")
2
Исключение AttributeError.
Ошибка импорта (ModuleNotFoundError)
Ошибка импорта вызывается при попытке импортировать несуществующий (или неспособный загрузиться) модуль в стандартном пути или даже при допущенной ошибке в имени.
import nibabel
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
in
----> 1 import nibabel
ModuleNotFoundError: No module named 'nibabel'
Ошибка поиска (LookupError)
LockupError
выступает базовым классом для исключений, которые происходят, когда key
или index
используются для связывания или последовательность списка/словаря неверна или не существует.
Здесь есть два вида исключений:
- Ошибка индекса (
IndexError
); - Ошибка ключа (
KeyError
);
Ошибка ключа
Если ключа, к которому нужно получить доступ, не оказывается в словаре, вызывается исключение KeyError
.
try:
a = {1:'a', 2:'b', 3:'c'}
print(a[4])
except LookupError:
print("Исключение KeyError.")
else:
print("Успех, нет ошибок!")
Исключение KeyError.
Ошибка индекса
Если пытаться получить доступ к индексу (последовательности) списка, которого не существует в этом списке или находится вне его диапазона, будет вызвана ошибка индекса (IndexError: list index out of range python).
try:
a = ['a', 'b', 'c']
print(a[4])
except LookupError:
print("Исключение IndexError, индекс списка вне диапазона.")
else:
print("Успех, нет ошибок!")
Исключение IndexError, индекс списка вне диапазона.
Ошибка памяти (MemoryError)
Как уже упоминалось, ошибка памяти вызывается, когда операции не хватает памяти для выполнения.
Ошибка имени (NameError)
Ошибка имени возникает, когда локальное или глобальное имя не находится.
В следующем примере переменная ans
не определена. Результатом будет ошибка NameError
.
try:
print(ans)
except NameError:
print("NameError: переменная 'ans' не определена")
else:
print("Успех, нет ошибок!")
NameError: переменная 'ans' не определена
Ошибка выполнения (Runtime Error)
Ошибка «NotImplementedError»
Ошибка выполнения служит базовым классом для ошибки NotImplemented
. Абстрактные методы определенного пользователем класса вызывают это исключение, когда производные методы перезаписывают оригинальный.
class BaseClass(object):
"""Опередляем класс"""
def __init__(self):
super(BaseClass, self).__init__()
def do_something(self):
# функция ничего не делает
raise NotImplementedError(self.__class__.__name__ + '.do_something')
class SubClass(BaseClass):
"""Реализует функцию"""
def do_something(self):
# действительно что-то делает
print(self.__class__.__name__ + ' что-то делает!')
SubClass().do_something()
BaseClass().do_something()
SubClass что-то делает!
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
in
14
15 SubClass().do_something()
---> 16 BaseClass().do_something()
in do_something(self)
5 def do_something(self):
6 # функция ничего не делает
----> 7 raise NotImplementedError(self.__class__.__name__ + '.do_something')
8
9 class SubClass(BaseClass):
NotImplementedError: BaseClass.do_something
Ошибка типа (TypeError)
Ошибка типа вызывается при попытке объединить два несовместимых операнда или объекта.
В примере ниже целое число пытаются добавить к строке, что приводит к ошибке типа.
try:
a = 5
b = "PythonRu"
c = a + b
except TypeError:
print('Исключение TypeError')
else:
print('Успех, нет ошибок!')
Исключение TypeError
Ошибка значения (ValueError)
Ошибка значения вызывается, когда встроенная операция или функция получают аргумент с корректным типом, но недопустимым значением.
В этом примере встроенная операция float
получат аргумент, представляющий собой последовательность символов (значение), что является недопустимым значением для типа: число с плавающей точкой.
try:
print(float('PythonRu'))
except ValueError:
print('ValueError: не удалось преобразовать строку в float: 'PythonRu'')
else:
print('Успех, нет ошибок!')
ValueError: не удалось преобразовать строку в float: 'PythonRu'
Пользовательские исключения в Python
В Python есть много встроенных исключений для использования в программе. Но иногда нужно создавать собственные со своими сообщениями для конкретных целей.
Это можно сделать, создав новый класс, который будет наследовать из класса Exception
в Python.
class UnAcceptedValueError(Exception):
def __init__(self, data):
self.data = data
def __str__(self):
return repr(self.data)
Total_Marks = int(input("Введите общее количество баллов: "))
try:
Num_of_Sections = int(input("Введите количество разделов: "))
if(Num_of_Sections < 1):
raise UnAcceptedValueError("Количество секций не может быть меньше 1")
except UnAcceptedValueError as e:
print("Полученная ошибка:", e.data)
Введите общее количество баллов: 10
Введите количество разделов: 0
Полученная ошибка: Количество секций не может быть меньше 1
В предыдущем примере если ввести что-либо меньше 1, будет вызвано исключение. Многие стандартные исключения имеют собственные исключения, которые вызываются при возникновении проблем в работе их функций.
Недостатки обработки исключений в Python
У использования исключений есть свои побочные эффекты, как, например, то, что программы с блоками try-except работают медленнее, а количество кода возрастает.
Дальше пример, где модуль Python timeit
используется для проверки времени исполнения 2 разных инструкций. В stmt1
для обработки ZeroDivisionError
используется try-except, а в stmt2
— if
. Затем они выполняются 10000 раз с переменной a=0
. Суть в том, чтобы показать разницу во времени исполнения инструкций. Так, stmt1
с обработкой исключений занимает больше времени чем stmt2
, который просто проверяет значение и не делает ничего, если условие не выполнено.
Поэтому стоит ограничить использование обработки исключений в Python и применять его в редких случаях. Например, когда вы не уверены, что будет вводом: целое или число с плавающей точкой, или не уверены, существует ли файл, который нужно открыть.
import timeit
setup="a=0"
stmt1 = '''
try:
b=10/a
except ZeroDivisionError:
pass'''
stmt2 = '''
if a!=0:
b=10/a'''
print("time=",timeit.timeit(stmt1,setup,number=10000))
print("time=",timeit.timeit(stmt2,setup,number=10000))
time= 0.003897680000136461
time= 0.0002797570000439009
Выводы!
Как вы могли увидеть, обработка исключений помогает прервать типичный поток программы с помощью специального механизма, который делает код более отказоустойчивым.
Обработка исключений — один из основных факторов, который делает код готовым к развертыванию. Это простая концепция, построенная всего на 4 блоках: try
выискивает исключения, а except
их обрабатывает.
Очень важно поупражняться в их использовании, чтобы сделать свой код более отказоустойчивым.
Содержание
- Pycharm выдает ошибку «не удается найти модуль __main__» »
- Установите Python, Numpy, Matplotlib, Scipy в Windows
- How to fix python can’t find ‘__main__’ module error in easy 2 ways?
- What is __main__ in python?
- Why does the error occur?
- Code example 1
- Code example 2
- How to fix the python can’t find ‘__main__’ module error?
- Add the main module to your script
- Move your script to the same directory as your main module
- Conclusion
- Python Error Can’t Find Main Module
- Resolve the can’t find ‘__main__’ module Error in Python
- Conclusion
- Работа с ошибками в Python
- Заголовок
- Чтение Traceback 1
- Чтение Traceback 2
- Некоторые ошибки с примерами кода
- Ошибки в синтаксисе
- Ошибки в логике
Pycharm выдает ошибку «не удается найти модуль __main__» »
Установите Python, Numpy, Matplotlib, Scipy в Windows
Всякий раз, когда я пытаюсь запустить скрипт через Virtualenv в pycharm, я получаю такую ошибку:
Все работает нормально через idle или vs code. Я полагаю, что это должно быть что-то с тем, как я настроил свой pycharm, но понятия не имею, что.
edit: это происходит независимо от того, что я запускаю, даже с простой функцией печати.
изменить: даже при выборе обычного интерпретатора Python, то же самое происходит только в pycharm
- 3 Можете ли вы показать нам свой код?
- Что бы я ни бегал. Даже пробовал простую печать («бла бла») prntscr.com/kn60cu
- 1 Как вы запускаете код? Это не похоже на файл Python, он не заканчивается на .py
- Он заканчивается на py, и это файл python, вы можете видеть, что он заканчивается на test.py и значок python рядом с файлом. Разобрался в чем дело, отправил ответ. спасибо, ребята
Разобрался в чем дело. В окне конфигурации в pycharm мне нужно было выбрать правильный путь к скрипту:
- 2 Путь к сценарию вводит в заблуждение (ИМХО), поскольку он также должен включать файл сценария.
В вашем Pycharm:
- Выбрать Run — Edit Configurations
- В Configuration tabs , Выбрать Module name в опции Choose target to run и введите имя вашего файла python
- Нажмите Apply а также OK кнопка
Или простой способ — когда вы запускаете свой код в первый раз (в новом файле), просто введите клавиатуру Alt+Shift+F10 для запуска и сохранения конфигурации. Во второй раз (после сохранения конфигурации) просто введите Alt+F10 для запуска вашего кода.
- также называется «путь к сценарию»
Перейдите в «Редактировать конфигурацию» и укажите только свое имя файла, например filename.py
Существующий путь ——> C:Usersnp4PycharmProjectsTESTvenv
Попробуйте с этим ——> C:Usersnp4PycharmProjectsTESTvenvMultiSites.py
Я столкнулся с этой проблемой, когда мой cmd был вынужден «Завершить задачу» диспетчером задач. Моя проблема решена, когда я перезапускаю свою IDE.
«Открыть диалоговое окно« Редактировать настройки запуска / отладки »» (вверху, рядом с «Выполнить») «Изменить конфигурации» «Путь к сценарию:» -> выберите правильный путь к сценарию.
В Pycharm (Ubuntu):
- Создать новый проект
- Дайте название проекта
- Щелкните правой кнопкой мыши папку bin
- Создать новый файл Python
- Напишите свой код
- Верхняя правая сторона: Добавить конфигурацию
- Левая сторона: щелкните правой кнопкой мыши знак «+»
- Введите полное имя файла
- Home / Downloads / myfile.py как «Путь к сценарию»
- python 2.x / 3.x как интерпретатор Python
- Нажмите «Применить / ОК»
Я исправил это, удалив значения из Возможности переводчика в конфигурациях запуска / отладки. Пытаясь добавить интерпретатор, я добавил путь Python к указанному полю.
После расчистки поля все наладилось.
Ты можешь найти run/debug configuration настройки в раскрывающемся списке слева от значка запуска в правом верхнем углу окна pycharm.
Источник
How to fix python can’t find ‘__main__’ module error in easy 2 ways?
Python can’t find ‘__main__’ module is one of the most common errors that can occur when running a Python script. We’ll cover why this error occurs and provide two different code examples that demonstrate how it can happen. We’ll also show you how to fix the error so that you can continue developing your web applications with Python.
What is __main__ in python?
__main__ is a special name in Python that is used to indicate the source file where the program execution starts. Every Python program has a ‘__main__’ module, and the code within that module is executed when the program is run. The ‘__main__’ module can be located in any file, but it is typically placed in a file named ‘ main.py ‘.
For example, consider the following ‘main.py’ file:
When this program is run, the output will be as follows:
Why does the error occur?
There are two main reasons why the python can’t find ‘__main__’ module error might occur. The first reason is that you haven’t specified the main module in your Python script. The second reason is that your main module is not located in the same directory as your Python script. We’ll discuss each of these reasons in more detail below.
Code example 1
Let’s say you have a Python script named my_script.py and it contains the following code:
If you try to run this script, you’ll get the python can’t find ‘__main__’ module error
This is because we haven’t specified the main module in our script. Python will look for a module named __main__ and, if it can’t find one, it will throw an error. To fix this, we need to add the main module to our script. We can do this by wrapping our code in an if __name__ == «__main__»: block:
Now when we run our script, we get the expected output:
Code example 2
Let’s say you have a Python script named my_script.py and it contains the following code:
And you also have a Python module named some_module.py that contains the following code:
If you try to run my_script.py , you’ll get the following error:
This is because your main module ( my_script.py ) is not located in the same directory as your Python script. When Python tries to import some_module , it will look for it in the current directory (the directory where my_script.py is located). Since it can’t find it there, it will throw an error. You can fix this by moving your Python script to the same directory as your main module:
Now when you run my_script.py , it will be able to find and import some_module.py without any problems.
How to fix the python can’t find ‘__main__’ module error?
As we’ve seen in the examples above, there are two ways to fix the “python can’t find ‘__main__’ module” error. The first way is to add the main module to your Python script. The second way is to move your Python script to the same directory as your main module. Below, we’ll go through both of these strategies in further depth.
Add the main module to your script
If you get this error, it means that you haven’t specified the main module in your Python script. You can fix this by wrapping your code in an if __name__ == «__main__»: block:
When you run your script, Python will look for a module named __main__ and, if it can’t find one, it will throw an error. This block of code will ensure that Python can find your main module and avoid errors like python can’t find ‘__main__’ module
Move your script to the same directory as your main module
If you get this error, it means that your main module is not located in the same directory as your Python script. You can fix this by moving your Python script to the same directory as your main module:
When you run your Python script, it will look for modules in the current directory (the directory where my_script.py is located). By moving my_script.py to the same directory as we’re ensuring that our script can find all of the modules it needs.
Conclusion
In this article, we’ve discussed the “ python can’t find ‘__main__’ module ” error. We’ve seen two different code examples that demonstrate how this error can occur. And we’ve also shown you how to fix the error by either adding the main module to your script or moving your script to the same directory as your main module so that you can continue developing your web applications.
Источник
Python Error Can’t Find Main Module
In this article, we’ll discuss the error can’t find ‘__main__’ module , the causes of it, and how to resolve the error in Python.
Resolve the can’t find ‘__main__’ module Error in Python
We wrote a simple code to print in the PyCharm environment. As we see in the top right corner, the play button or running button is disabled, which means there is no way to run this code.
To be able to read this code, we need to add a configuration or add an interpreter, and then it gives us an execution. But if we click the play button, the program does not run.
The problem is that we don’t have an interpreter to run the codes on PyCharm, or we get an error when running the created interpreter. In our case, we created an interpreter, but still, the code doesn’t run.
So what caused the issue? The first reason is there is no virtual environment, and the second is the Python interpreter cannot find the project folder.
We need to check if we have installed Python on our system to resolve this issue. In the next step, we create a virtual environment for our project and specify the Python file we want to read.
To check the Python is installed or not, we open our terminal and type “python” and hit enter if you have Python installed on your system, it gives you the version of Python, and if nothing pops up, that means you do not have Python installed on our system. You need to download Python from here.
Let’s create a virtual environment for our project and create an empty folder. After that, we go to the PyCharm environment to delete the interpreter.
- Click on the top-left corner file.
- Go to settings. It will open up a project for you and then go to your project.
- We click on the plus( + ) button where we specifically tell the machine where we want to create our virtual environment.
- Once the virtual environment is created, you must select it.
- Click «OK» then «Apply» .
In the next step, we will add configuration.
- For our project, click on add configuration .
- Click on add new and select Python .
- After opening a new window, look at the script path where we have to select our project file, so you have to go through your project or wherever you saved your folder.
- Once you pick up the script path or the project file, it will automatically pick up the working directory. If it doesn’t, just click on the folder, go to the project folder, and pick it up yourself.
- Then click «Apply» and «OK» .
- We will run the code to see if everything is working fine. Click on the play button, and the code is executed successfully here.
To make it short, when you get the can’t find ‘__main__’ module error in Python. We resolve it by doing the following:
- Adding the main module to your script.
- Moving your script to the same directory as your main module.
Conclusion
We’ve discussed in this article how to resolve the can’t find ‘__main__’ module error in Python.
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
Источник
Работа с ошибками в Python
Заголовок
Создайте файл solution.py со следующим кодом:
Наш код подразумевает печать содержимого переменной vector.
Запустим написанный скрипт, получим следующий вывод:
Сообщение означает, что при исполнении кода возникла ошибка. При этом Python сообщает нам кое-что ещё. Разберём это сообщение детально.
Чтение Traceback 1
Исходное сообщение нужно мысленно разделить на две части. Первая часть это traceback-сообщение:
Вторая часть — сообщение о возникшей ошибке:
Разберём первую часть. Traceback в грубом переводе означает «отследить назад». Traceback показывает последовательность/стэк вызовов, которая, в конечном итоге, вызвала ошибку.
является заголовочной. Она сообщает, что в последующих строках будет изложен стэк вызовов (он показан отступами). Обратите внимание на сообщение в скобках, оно указывает на порядок вызовов. В данном случае (он же случай по умолчанию) тот вызов, в котором произошла ошибка, будет в последовательности вызовов указан последним.
Вторая и третья строки:
показывают информацию о вызове (в нашем случае он один). Во-первых, здесь есть информация о файле, в котором произошёл вызов («solution.py»), затем указан номер строки, где этот вызов происходит («line 1″), в конце стоит информация о том, откуда произошёл вызов (» «). В нашем случае вызов происходит непосредственно из модуля, т.е. не из функции. Наконец, вывод содержит не только номер строки, но и саму строку «for coord in vector:».
Заключительная строка сообщения:
содержит вид (тип) ошибки («NameError»), и после двоеточия содержит подсказку. В данном случае она означает, что имя «vector» не определено.
В самом деле, если взглянуть снова на код, то можно убедиться, что мы нигде не объявили переменную «vector».
Подведём итоги. При попытке запуска мы получили следующий вывод
Он говорит нам о возникновении ошибки. Эта ошибка обнаружилась интерпретатором в первой строке файла «solution.py». Сама ошибка является ошибкой имени и указывает на необъявленное имя — «vector».
Чтение Traceback 2
Оберните код из solution.py в функцию:
Запустим наш код
На этот раз сообщение об ошибке сложнее, однако структура у него та же.
Часть со стеком вызовов увеличилась:
Поскольку «most recent call last», читать будем её сверху вниз.
Вызовов на этот раз два. Первый вызов:
Произошел в пятой строке. Судя по строчке кода, это вызов написанной нами функции print_vector(5) с аргументом 5.
Следом за ней второй вызов:
Этот вызов происходит внутри функции print_vector, содержащейся в файле «solution.py». Вызов находится в строке 2.
Сама же ошибка имеет вид:
Как и в первом примере, сообщение об ошибке содержит её тип и подсказку. В нашем случае произошла ошибка типа. В подсказке же указано, что объект типа int не является итерируемым, т.е. таким объектом, который нельзя использовать в цикле for.
В нашем коде возникла ошибка. Её вызвала последовательность вызовов. Первый вызов произошел непосредственно из модуля — в строке 5 происходит вызов функции print_vector(5). Внутри этой функции ошибка возникла в строчке 2, содержащей проход по циклу. Сообщение об ошибке означает, что итерироваться по объекту типа int нельзя. В нашем случае мы вызвали функцию print_vector от числа (от 5).
Некоторые ошибки с примерами кода
Ошибки в синтаксисе
Наиболее частая ошибка, которая возникает в программах на Python — SyntaxError: когда какое-то утверждение записано не по правилам языка, например:
Тот же тип ошибки возникнет, если забыть поставить двоеточие в цикле:
При неправильном использовании пробелов и табуляций в начале строки возникает IndentationError:
А теперь посмотрим, что будет, если в первой строке цикла воспользоваться пробелами, а во второй — табуляцией:
NameError возникает при обращении к несуществующей переменной:
Ошибки в логике
Напишем простую программу на деление с остатком и сохраним как sample.py:
Возникла ошибка TypeError, которая сообщает о неподходящем типе данных. Исправим программу:
запустим на неподходящих данных:
Возникнет ValueError. Эту ошибку ещё можно воспринимать как использование значения вне области допустимых значений (ОДЗ).
Теперь запустим программу на числовых данных:
При работе с массивами нередко возникает ошибка IndexError. Она возникает при выходе за пределы массива:
Что будет, если вызвать бесконечную рекурсию? Опишем её в программе endless.py
Через некоторое время после запуска возникнет RecursionError:
Источник
Python-tutorial
Handling Errors in Python
So we have scripts which run just fine as long as the information we give them is what they expect. Errors appear when the script/program encounters conditions it does not know how to handle. This is not as scary as it sounds and it is very nice to have scripts which you can feel confident are not going to “crash” as soon as someone else tries to use them.
What can go wrong?
Running your script on your own system may be working perfectly but is it also going to work on another system?
A brief list of what can commonly go wrong:
- Syntax errors such as typos or incorrect arguments
- Files needed by the script can’t be found or accessed
- A list created dynamically may not get created at all or items in the list expected at a certain index (location) are missing
- Values passed in as arguments may not be what was expected by the script
Syntax errors
As you create your script, it is very common to have syntax errors, that is, typos, incorrectly named methods or passing in the wrong number of arguments to a method. Catching these early is easier with an editor like Spyder.
- Run the following code and see the error — also note how Spyder highlights the problem.
if (1 == 1)
print("This is a syntax error")
You should see an output showing that something is missing (what should this be?):
if (1 == 1)
^
SyntaxError: invalid syntax
Syntax errors will prevent the code from running in the first place. Errors which only appear when your program is running are known as “Runtime Errors”. We will now see how to deal with this.
Catching errors
Fortunately, we can catch these errors and then do something about them before they abruptly end our program.
The structure is a “try-except-finally” block of code which is wrapped around your code.
try:
prnt("This is a name error") #misspelt print so no method found
except NameError as e:
print("Caught this error:", e)
finally:
print("I'm always going to run this bit")
The
finally
section is optional
“Continue anyway” errors
When running in a loop and the error is minor, such as the first line in a text file is blank and you just want to skip it, use continue
in the except
block to tell the program to go to the next instruction.
“Stop running if this occurs” errors
When running in a loop and the error is major and you cannot continue without the information:
- use
break
and the loop will end - use
pass
and the program will pass to the next instruction sys.exit()
exit()
quit()
andos._exit(0)
will kill the Python interpreter so avoid if possible or at least use with caution
Reading Files
So, we will see how to put this in a script which reads the gapminder data as we have done in previous lessons.
Files can be missing or not accessible so we will include error catching to notify us of these problems.
Previously:
import pandas
data = pandas.read_csv('data/gapminder_gdp_oceania.csv')
print(data)
- We will change the hard-coded filename to variables
- We will also use variable called
sep
from a library calledos
which separates filenames according to operating system so this code can be run on Mac, Unix or Windows
import pandas
from os import sep
filename = datadir + sep + fname
data = pandas.read_csv(filename)
print(data)
- Now put the code in a
try:
block
import pandas
from os import sep
inputdir = "data"
fname = "gapminder_gdp_oceania.csv"
try:
filename = inputdir + sep + fname
data = pandas.read_csv(filename)
if (not data.empty):
print(data.iloc[0, 0])
except OSError as e:
print("ERROR: Unable to find or access file:", e)
pass
Looping through a directory
Often, you will be using a Python script to read files in a directory.
- We will add a loop for our files:
inputdir = "data"
try:
for fname in listdir(inputdir):
print(fname)
filename = inputdir + sep + fname
data = pandas.read_csv(filename)
if (not data.empty):
print(data.iloc[0, 0])
except OSError as e:
print("ERROR: Unable to find or access file:", e)
pass
Making a dynamic script
Finally, we will allow the directory to be loaded from outside the script.
- Now we will incorporate our argument parser from the previous lesson so the directory can be specified when we run the script rather than having to change the code
- Don’t forget the special line
if __name__ == '__main__':
so we can run from the terminal console
import pandas
import argparse
from os import sep, listdir
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='Read CSV Files',
description='''Reads a directory and extracts first cell from each file''')
parser.add_argument('--filedir', action='store', help='Directory containing files', default="data")
args = parser.parse_args()
inputdir = args.filedir
try:
for fname in listdir(inputdir):
print(fname)
filename = inputdir + sep + fname
data = pandas.read_csv(filename)
if (not data.empty):
print(data.iloc[0, 0])
except OSError as e:
print("ERROR: Unable to find or access file:", e)
pass
Challenge
Take this one step further and put your code into methods.
Challenge
Combine all our csv files into an excel file.
TIPS
- The
glob
library is very good at filtering files from a directory based on their filenames. For example, selecting all CSV files, or all files with numbers in the filename.files = glob.glob('data/*.csv')
— the data directory can be replaced by an argument
- Pandas has a method to write to an Excel file called
to_excel
and you will need a special file writer calledExcelWriter
from the pandas librarywriter = pandas.ExcelWriter(outputfile, engine='xlsxwriter')
— sets up the file writer(fsheet, _) = splitext(basename(f2))
— extract just the filename from the file usingbasename()
fromos.path
librarydata.to_excel(writer, sheet_name=fsheet)
— write the data to the writer with an Excel sheet named as the filename
import argparse
import glob
from os import sep, listdir, R_OK, access
from os.path import join, basename, splitext
import pandas
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='Combine CSV files to an Excel file',
description='''
Reads a directory and combines csv files into an excel file
''')
parser.add_argument('--filedir', action='store', help='Directory containing files', default="data")
parser.add_argument('--output', action='store', help='Output file name', default="output.xlsx")
args = parser.parse_args()
inputdir = args.filedir
outputfile = args.filedir + sep + args.output
print("Input directory:", inputdir)
if access(inputdir, R_OK):
seriespattern = '*.csv'
writer = pandas.ExcelWriter(outputfile, engine='xlsxwriter')
try:
files = glob.glob(join(inputdir, seriespattern))
print("Files:", len(files))
for f2 in files:
print(f2)
(fsheet, _) = splitext(basename(f2))
data = pandas.read_csv(f2)
if (not data.empty):
data.to_excel(writer, sheet_name=fsheet)
print("Files combined to: ", outputfile)
except ValueError as e:
print("ERROR: ", e)
except OSError as e:
print("ERROR: Unable to find or access file:", e)
pass
finally:
writer.save()
writer.close()
else:
print("Cannot access directory: ", inputdir)
Errors¶
Errors or mistakes in a program are often referred to as bugs. They are almost always the fault of the programmer. The process of finding and eliminating errors is called debugging. Errors can be classified into three major groups:
- Syntax errors
- Runtime errors
- Logical errors
Syntax errors¶
Python will find these kinds of errors when it tries to parse your program, and exit with an error message without running anything. Syntax errors are mistakes in the use of the Python language, and are analogous to spelling or grammar mistakes in a language like English: for example, the sentence Would you some tea? does not make sense – it is missing a verb.
Common Python syntax errors include:
- leaving out a keyword
- putting a keyword in the wrong place
- leaving out a symbol, such as a colon, comma or brackets
- misspelling a keyword
- incorrect indentation
- empty block
Note
it is illegal for any block (like an if
body, or the body of a function) to be left completely empty. If you want a block to do nothing, you can use the pass
statement inside the block.
Python will do its best to tell you where the error is located, but sometimes its messages can be misleading: for example, if you forget to escape a quotation mark inside a string you may get a syntax error referring to a place later in your code, even though that is not the real source of the problem. If you can’t see anything wrong on the line specified in the error message, try backtracking through the previous few lines. As you program more, you will get better at identifying and fixing errors.
Here are some examples of syntax errors in Python:
myfunction(x, y): return x + y else: print("Hello!") if mark >= 50 print("You passed!") if arriving: print("Hi!") esle: print("Bye!") if flag: print("Flag is set!")
Runtime errors¶
If a program is syntactically correct – that is, free of syntax errors – it will be run by the Python interpreter. However, the program may exit unexpectedly during execution if it encounters a runtime error – a problem which was not detected when the program was parsed, but is only revealed when a particular line is executed. When a program comes to a halt because of a runtime error, we say that it has crashed.
Consider the English instruction flap your arms and fly to Australia. While the instruction is structurally correct and you can understand its meaning perfectly, it is impossible for you to follow it.
Some examples of Python runtime errors:
- division by zero
- performing an operation on incompatible types
- using an identifier which has not been defined
- accessing a list element, dictionary value or object attribute which doesn’t exist
- trying to access a file which doesn’t exist
Runtime errors often creep in if you don’t consider all possible values that a variable could contain, especially when you are processing user input. You should always try to add checks to your code to make sure that it can deal with bad input and edge cases gracefully. We will look at this in more detail in the chapter about exception handling.
Logical errors¶
Logical errors are the most difficult to fix. They occur when the program runs without crashing, but produces an incorrect result. The error is caused by a mistake in the program’s logic. You won’t get an error message, because no syntax or runtime error has occurred. You will have to find the problem on your own by reviewing all the relevant parts of your code – although some tools can flag suspicious code which looks like it could cause unexpected behaviour.
Sometimes there can be absolutely nothing wrong with your Python implementation of an algorithm – the algorithm itself can be incorrect. However, more frequently these kinds of errors are caused by programmer carelessness. Here are some examples of mistakes which lead to logical errors:
- using the wrong variable name
- indenting a block to the wrong level
- using integer division instead of floating-point division
- getting operator precedence wrong
- making a mistake in a boolean expression
- off-by-one, and other numerical errors
If you misspell an identifier name, you may get a runtime error or a logical error, depending on whether the misspelled name is defined.
A common source of variable name mix-ups and incorrect indentation is frequent copying and pasting of large blocks of code. If you have many duplicate lines with minor differences, it’s very easy to miss a necessary change when you are editing your pasted lines. You should always try to factor out excessive duplication using functions and loops – we will look at this in more detail later.
Exercise 1¶
-
Find all the syntax errors in the code snippet above, and explain why they are errors.
-
Find potential sources of runtime errors in this code snippet:
dividend = float(input("Please enter the dividend: ")) divisor = float(input("Please enter the divisor: ")) quotient = dividend / divisor quotient_rounded = math.round(quotient)
-
Find potential sources of runtime errors in this code snippet:
for x in range(a, b): print("(%f, %f, %f)" % my_list[x])
-
Find potential sources of logic errors in this code snippet:
product = 0 for i in range(10): product *= i sum_squares = 0 for i in range(10): i_sq = i**2 sum_squares += i_sq nums = 0 for num in range(10): num += num
Handling exceptions¶
Until now, the programs that we have written have generally ignored the fact that things can go wrong. We have have tried to prevent runtime errors by checking data which may be incorrect before we used it, but we haven’t yet seen how we can handle errors when they do occur – our programs so far have just crashed suddenly whenever they have encountered one.
There are some situations in which runtime errors are likely to occur. Whenever we try to read a file or get input from a user, there is a chance that something unexpected will happen – the file may have been moved or deleted, and the user may enter data which is not in the right format. Good programmers should add safeguards to their programs so that common situations like this can be handled gracefully – a program which crashes whenever it encounters an easily foreseeable problem is not very pleasant to use. Most users expect programs to be robust enough to recover from these kinds of setbacks.
If we know that a particular section of our program is likely to cause an error, we can tell Python what to do if it does happen. Instead of letting the error crash our program we can intercept it, do something about it, and allow the program to continue.
All the runtime (and syntax) errors that we have encountered are called exceptions in Python – Python uses them to indicate that something exceptional has occurred, and that your program cannot continue unless it is handled. All exceptions are subclasses of the Exception
class – we will learn more about classes, and how to write your own exception types, in later chapters.
The try
and except
statements¶
To handle possible exceptions, we use a try-except block:
try: age = int(input("Please enter your age: ")) print("I see that you are %d years old." % age) except ValueError: print("Hey, that wasn't a number!")
Python will try to process all the statements inside the try block. If a ValueError
occurs at any point as it is executing them, the flow of control will immediately pass to the except block, and any remaining statements in the try block will be skipped.
In this example, we know that the error is likely to occur when we try to convert the user’s input to an integer. If the input string is not a number, this line will trigger a ValueError
– that is why we specified it as the type of error that we are going to handle.
We could have specified a more general type of error – or even left the type out entirely, which would have caused the except
clause to match any kind of exception – but that would have been a bad idea. What if we got a completely different error that we hadn’t predicted? It would be handled as well, and we wouldn’t even notice that anything unusual was going wrong. We may also want to react in different ways to different kinds of errors. We should always try pick specific rather than general error types for our except
clauses.
It is possible for one except
clause to handle more than one kind of error: we can provide a tuple of exception types instead of a single type:
try: dividend = int(input("Please enter the dividend: ")) divisor = int(input("Please enter the divisor: ")) print("%d / %d = %f" % (dividend, divisor, dividend/divisor)) except(ValueError, ZeroDivisionError): print("Oops, something went wrong!")
A try-except block can also have multiple except
clauses. If an exception occurs, Python will check each except
clause from the top down to see if the exception type matches. If none of the except
clauses match, the exception will be considered unhandled, and your program will crash:
try: dividend = int(input("Please enter the dividend: ")) divisor = int(input("Please enter the divisor: ")) print("%d / %d = %f" % (dividend, divisor, dividend/divisor)) except ValueError: print("The divisor and dividend have to be numbers!") except ZeroDivisionError: print("The dividend may not be zero!")
Note that in the example above if a ValueError
occurs we won’t know whether it was caused by the dividend or the divisor not being an integer – either one of the input lines could cause that error. If we want to give the user more specific feedback about which input was wrong, we will have to wrap each input line in a separate try-except block:
try: dividend = int(input("Please enter the dividend: ")) except ValueError: print("The dividend has to be a number!") try: divisor = int(input("Please enter the divisor: ")) except ValueError: print("The divisor has to be a number!") try: print("%d / %d = %f" % (dividend, divisor, dividend/divisor)) except ZeroDivisionError: print("The dividend may not be zero!")
In general, it is a better idea to use exception handlers to protect small blocks of code against specific errors than to wrap large blocks of code and write vague, generic error recovery code. It may sometimes seem inefficient and verbose to write many small try-except statements instead of a single catch-all statement, but we can mitigate this to some extent by making effective use of loops and functions to reduce the amount of code duplication.
How an exception is handled¶
When an exception occurs, the normal flow of execution is interrupted. Python checks to see if the line of code which caused the exception is inside a try block. If it is, it checks to see if any of the except blocks associated with the try block can handle that type of exception. If an appropriate handler is found, the exception is handled, and the program continues from the next statement after the end of that try-except.
If there is no such handler, or if the line of code was not in a try block, Python will go up one level of scope: if the line of code which caused the exception was inside a function, that function will exit immediately, and the line which called the function will be treated as if it had thrown the exception. Python will check if that line is inside a try block, and so on. When a function is called, it is placed on Python’s stack, which we will discuss in the chapter about functions. Python traverses this stack when it tries to handle an exception.
If an exception is thrown by a line which is in the main body of your program, not inside a function, the program will terminate. When the exception message is printed, you should also see a traceback – a list which shows the path the exception has taken, all the way back to the original line which caused the error.
Error checks vs exception handling¶
Exception handling gives us an alternative way to deal with error-prone situations in our code. Instead of performing more checks before we do something to make sure that an error will not occur, we just try to do it – and if an error does occur we handle it. This can allow us to write simpler and more readable code. Let’s look at a more complicated input example – one in which we want to keep asking the user for input until the input is correct. We will try to write this example using the two different approaches:
# with checks n = None while n is None: s = input("Please enter an integer: ") if s.lstrip('-').isdigit(): n = int(s) else: print("%s is not an integer." % s) # with exception handling n = None while n is None: try: s = input("Please enter an integer: ") n = int(s) except ValueError: print("%s is not an integer." % s)
In the first code snippet, we have to write quite a convoluted check to test whether the user’s input is an integer – first we strip off a minus sign if it exists, and then we check if the rest of the string consists only of digits. But there’s a very simple criterion which is also what we really want to know: will this string cause a ValueError
if we try to convert it to an integer? In the second snippet we can in effect check for exactly the right condition instead of trying to replicate it ourselves – something which isn’t always easy to do. For example, we could easily have forgotten that integers can be negative, and written the check in the first snippet incorrectly.
Here are a few other advantages of exception handling:
- It separates normal code from code that handles errors.
- Exceptions can easily be passed along functions in the stack until they reach a function which knows how to handle them. The intermediate functions don’t need to have any error-handling code.
- Exceptions come with lots of useful error information built in – for example, they can print a traceback which helps us to see exactly where the error occurred.
The else
and finally
statements¶
There are two other clauses that we can add to a try-except block: else
and finally
. else
will be executed only if the try
clause doesn’t raise an exception:
try: age = int(input("Please enter your age: ")) except ValueError: print("Hey, that wasn't a number!") else: print("I see that you are %d years old." % age)
We want to print a message about the user’s age only if the integer conversion succeeds. In the first exception handler example, we put this print statement directly after the conversion inside the try
block. In both cases, the statement will only be executed if the conversion statement doesn’t raise an exception, but putting it in the else
block is better practice – it means that the only code inside the try
block is the single line that is the potential source of the error that we want to handle.
When we edit this program in the future, we may introduce additional statements that should also be executed if the age input is successfully converted. Some of these statements may also potentially raise a ValueError
. If we don’t notice this, and put them inside the try
clause, the except
clause will also handle these errors if they occur. This is likely to cause some odd and unexpected behaviour. By putting all this extra code in the else
clause instead, we avoid taking this risk.
The finally
clause will be executed at the end of the try-except block no matter what – if there is no exception, if an exception is raised and handled, if an exception is raised and not handled, and even if we exit the block using break
, continue
or return
. We can use the finally
clause for cleanup code that we always want to be executed:
try: age = int(input("Please enter your age: ")) except ValueError: print("Hey, that wasn't a number!") else: print("I see that you are %d years old." % age) finally: print("It was really nice talking to you. Goodbye!")
Exercise 2¶
-
Extend the program in exercise 7 of the loop control statements chapter to include exception handling. Whenever the user enters input of the incorrect type, keep prompting the user for the same value until it is entered correctly. Give the user sensible feedback.
-
Add a try-except statement to the body of this function which handles a possible
IndexError
, which could occur if the index provided exceeds the length of the list. Print an error message if this happens:def print_list_element(thelist, index): print(thelist[index])
-
This function adds an element to a list inside a dict of lists. Rewrite it to use a try-except statement which handles a possible
KeyError
if the list with the name provided doesn’t exist in the dictionary yet, instead of checking beforehand whether it does. Includeelse
andfinally
clauses in your try-except block:def add_to_list_in_dict(thedict, listname, element): if listname in thedict: l = thedict[listname] print("%s already has %d elements." % (listname, len(l))) else: thedict[listname] = [] print("Created %s." % listname) thedict[listname].append(element) print("Added %s to %s." % (element, listname))
The with
statement¶
Using the exception object¶
Python’s exception objects contain more information than just the error type. They also come with some kind of message – we have already seen some of these messages displayed when our programs have crashed. Often these messages aren’t very user-friendly – if we want to report an error to the user we usually need to write a more descriptive message which explains how the error is related to what the user did. For example, if the error was caused by incorrect input, it is helpful to tell the user which of the input values was incorrect.
Sometimes the exception message contains useful information which we want to display to the user. In order to access the message, we need to be able to access the exception object. We can assign the object to a variable that we can use inside the except
clause like this:
try: age = int(input("Please enter your age: ")) except ValueError as err: print(err)
err
is not a string, but Python knows how to convert it into one – the string representation of an exception is the message, which is exactly what we want. We can also combine the exception message with our own message:
try: age = int(input("Please enter your age: ")) except ValueError as err: print("You entered incorrect age input: %s" % err)
Note that inserting a variable into a formatted string using %s
also converts the variable to a string.
Raising exceptions¶
We can raise exceptions ourselves using the raise
statement:
try: age = int(input("Please enter your age: ")) if age < 0: raise ValueError("%d is not a valid age. Age must be positive or zero.") except ValueError as err: print("You entered incorrect age input: %s" % err) else: print("I see that you are %d years old." % age)
We can raise our own ValueError
if the age input is a valid integer, but it’s negative. When we do this, it has exactly the same effect as any other exception – the flow of control will immediately exit the try
clause at this point and pass to the except
clause. This except
clause can match our exception as well, since it is also a ValueError
.
We picked ValueError
as our exception type because it’s the most appropriate for this kind of error. There’s nothing stopping us from using a completely inappropriate exception class here, but we should try to be consistent. Here are a few common exception types which we are likely to raise in our own code:
TypeError
: this is an error which indicates that a variable has the wrong type for some operation. We might raise it in a function if a parameter is not of a type that we know how to handle.ValueError
: this error is used to indicate that a variable has the right type but the wrong value. For example, we used it whenage
was an integer, but the wrong kind of integer.NotImplementedError
: we will see in the next chapter how we use this exception to indicate that a class’s method has to be implemented in a child class.
We can also write our own custom exception classes which are based on existing exception classes – we will see some examples of this in a later chapter.
Something we may want to do is raise an exception that we have just intercepted – perhaps because we want to handle it partially in the current function, but also want to respond to it in the code which called the function:
try: age = int(input("Please enter your age: ")) except ValueError as err: print("You entered incorrect age input: %s" % err) raise err
Exercise 3¶
- Rewrite the program from the first question of exercise 2 so that it prints the text of Python’s original exception inside the
except
clause instead of a custom message. - Rewrite the program from the second question of exercise 2 so that the exception which is caught in the
except
clause is re-raised after the error message is printed.
Debugging programs¶
Syntax errors are usually quite straightforward to debug: the error message shows us the line in the file where the error is, and it should be easy to find it and fix it.
Runtime errors can be a little more difficult to debug: the error message and the traceback can tell us exactly where the error occurred, but that doesn’t necessarily tell us what the problem is. Sometimes they are caused by something obvious, like an incorrect identifier name, but sometimes they are triggered by a particular state of the program – it’s not always clear which of many variables has an unexpected value.
Logical errors are the most difficult to fix because they don’t cause any errors that can be traced to a particular line in the code. All that we know is that the code is not behaving as it should be – sometimes tracking down the area of the code which is causing the incorrect behaviour can take a long time.
It is important to test your code to make sure that it behaves the way that you expect. A quick and simple way of testing that a function is doing the right thing, for example, is to insert a print statement after every line which outputs the intermediate results which were calculated on that line. Most programmers intuitively do this as they are writing a function, or perhaps if they need to figure out why it isn’t doing the right thing:
def hypotenuse(x, y): print("x is %f and y is %f" % (x, y)) x_2 = x**2 print(x_2) y_2 = y**2 print(y_2) z_2 = x_2 + y_2 print(z_2) z = math.sqrt(z_2) print(z) return z
This is a quick and easy thing to do, and even experienced programmers are guilty of doing it every now and then, but this approach has several disadvantages:
-
As soon as the function is working, we are likely to delete all the print statements, because we don’t want our program to print all this debugging information all the time. The problem is that code often changes – the next time we want to test this function we will have to add the print statements all over again.
-
To avoid rewriting the print statements if we happen to need them again, we may be tempted to comment them out instead of deleting them – leaving them to clutter up our code, and possibly become so out of sync that they end up being completely useless anyway.
-
To print out all these intermediate values, we had to spread out the formula inside the function over many lines. Sometimes it is useful to break up a calculation into several steps, if it is very long and putting it all on one line makes it hard to read, but sometimes it just makes our code unnecessarily verbose. Here is what the function above would normally look like:
def hypotenuse(x, y): return math.sqrt(x**2 + y**2)
How can we do this better? If we want to inspect the values of variables at various steps of a program’s execution, we can use a tool like pdb
. If we want our program to print out informative messages, possibly to a file, and we want to be able to control the level of detail at runtime without having to change anything in the code, we can use logging.
Most importantly, to check that our code is working correctly now and will keep working correctly, we should write a permanent suite of tests which we can run on our code regularly. We will discuss testing in more detail in a later chapter.
Logging¶
Sometimes it is valuable for a program to output messages to a console or a file as it runs. These messages can be used as a record of the program’s execution, and help us to find errors. Sometimes a bug occurs intermittently, and we don’t know what triggers it – if we only add debugging output to our program when we want to begin an active search for the bug, we may be unable to reproduce it. If our program logs messages to a file all the time, however, we may find that some helpful information has been recorded when we check the log after the bug has occurred.
Some kinds of messages are more important than others – errors are noteworthy events which should almost always be logged. Messages which record that an operation has been completed successfully may sometimes be useful, but are not as important as errors. Detailed messages which debug every step of a calculation can be interesting if we are trying to debug the calculation, but if they were printed all the time they would fill the console with noise (or make our log file really, really big).
We can use Python’s logging
module to add logging to our program in an easy and consistent way. Logging statements are almost like print statements, but whenever we log a message we specify a level for the message. When we run our program, we set a desired log level for the program. Only messages which have a level greater than or equal to the level which we have set will appear in the log. This means that we can temporarily switch on detailed logging and switch it off again just by changing the log level in one place.
There is a consistent set of logging level names which most languages use. In order, from the highest value (most severe) to the lowest value (least severe), they are:
- CRITICAL – for very serious errors
- ERROR – for less serious errors
- WARNING – for warnings
- INFO – for important informative messages
- DEBUG – for detailed debugging messages
These names are used for integer constants defined in the logging
module. The module also provides methods which we can use to log messages. By default these messages are printed to the console, and the default log level is WARNING
. We can configure the module to customise its behaviour – for example, we can write the messages to a file instead, raise or lower the log level and change the message format. Here is a simple logging example:
import logging # log messages to a file, ignoring anything less severe than ERROR logging.basicConfig(filename='myprogram.log', level=logging.ERROR) # these messages should appear in our file logging.error("The washing machine is leaking!") logging.critical("The house is on fire!") # but these ones won't logging.warning("We're almost out of milk.") logging.info("It's sunny today.") logging.debug("I had eggs for breakfast.")
There’s also a special exception
method which is used for logging exceptions. The level used for these messages is ERROR
, but additional information about the exception is added to them. This method is intended to be used inside exception handlers instead of error
:
try: age = int(input("How old are you? ")) except ValueError as err: logging.exception(err)
If we have a large project, we may want to set up a more complicated system for logging – perhaps we want to format certain messages differently, log different messages to different files, or log to multiple locations at the same time. The logging module also provides us with logger and handler objects for this purpose. We can use multiple loggers to create our messages, customising each one independently. Different handlers are associated with different logging locations. We can connect up our loggers and handlers in any way we like – one logger can use many handlers, and multiple loggers can use the same handler.
Exercise 4¶
- Write logging configuration for a program which logs to a file called
log.txt
and discards all logs less important thanINFO
. - Rewrite the second program from exercise 2 so that it uses this logging configuration instead of printing messages to the console (except for the first print statement, which is the purpose of the function).
- Do the same with the third program from exercise 2.
Answers to exercises¶
Answer to exercise 1¶
-
There are five syntax errors:
- Missing
def
keyword in function definition else
clause without anif
- Missing colon after
if
condition - Spelling mistake (“esle”)
- The
if
block is empty because theprint
statement is not indented correctly
- Missing
-
- The values entered by the user may not be valid integers or floating-point numbers.
- The user may enter zero for the divisor.
- If the
math
library hasn’t been imported,math.round
is undefined.
-
a
,b
andmy_list
need to be defined before this snippet.- The attempt to access the list element with index
x
may fail during one of the loop iterations if the range froma
tob
exceeds the size ofmy_list
. - The string formatting operation inside the
print
statement expectsmy_list[x]
to be a tuple with three numbers. If it has too many or too few elements, or isn’t a tuple at all, the attempt to format the string will fail.
-
- If you are accumulating a number total by multiplication, not addition, you need to initialise the total to
1
, not0
, otherwise the product will always be zero! - The line which adds
i_sq
tosum_squares
is not aligned correctly, and will only add the last value ofi_sq
after the loop has concluded. - The wrong variable is used: at each loop iteration the current number in the range is added to itself and
nums
remains unchanged.
- If you are accumulating a number total by multiplication, not addition, you need to initialise the total to
Answer to exercise 2¶
-
Here is an example program:
person = {} properties = [ ("name", str), ("surname", str), ("age", int), ("height", float), ("weight", float), ] for property, p_type in properties: valid_value = None while valid_value is None: try: value = input("Please enter your %s: " % property) valid_value = p_type(value) except ValueError: print("Could not convert %s '%s' to type %s. Please try again." % (property, value, p_type.__name__)) person[property] = valid_value
-
Here is an example program:
def print_list_element(thelist, index): try: print(thelist[index]) except IndexError: print("The list has no element at index %d." % index)
-
Here is an example program:
def add_to_list_in_dict(thedict, listname, element): try: l = thedict[listname] except KeyError: thedict[listname] = [] print("Created %s." % listname) else: print("%s already has %d elements." % (listname, len(l))) finally: thedict[listname].append(element) print("Added %s to %s." % (element, listname))
Answer to exercise 3¶
-
Here is an example program:
person = {} properties = [ ("name", str), ("surname", str), ("age", int), ("height", float), ("weight", float), ] for property, p_type in properties: valid_value = None while valid_value is None: try: value = input("Please enter your %s: " % property) valid_value = p_type(value) except ValueError as ve: print(ve) person[property] = valid_value
-
Here is an example program:
def print_list_element(thelist, index): try: print(thelist[index]) except IndexError as ie: print("The list has no element at index %d." % index) raise ie
Answer to exercise 4¶
-
Here is an example of the logging configuration:
import logging logging.basicConfig(filename='log.txt', level=logging.INFO)
-
Here is an example program:
def print_list_element(thelist, index): try: print(thelist[index]) except IndexError: logging.error("The list has no element at index %d." % index)
-
Here is an example program:
def add_to_list_in_dict(thedict, listname, element): try: l = thedict[listname] except KeyError: thedict[listname] = [] logging.info("Created %s." % listname) else: logging.info("%s already has %d elements." % (listname, len(l))) finally: thedict[listname].append(element) logging.info("Added %s to %s." % (element, listname))
As you do more and more programming, you will naturally encounter a lot of errors (or bugs). Causing, understanding, and fixing errors is an important part of programming. Python will do its best to run anything that you tell it to run, but if it can’t understand what you’re asking, then it won’t run the program. All the same, Python will try to tell you a little bit of information about what went wrong, in order to help you try to fix it.
Here are two Python errors.
In this first example, we forget to use the parenthesis that are required by print(...)
. Python does not understand what you are trying to do.
Here is a second example of a bug in Python.
In the second example, we forget to define the greeting
variable. Python knows what you want it to do, but since no greeting
has been defined, an error occurs.
A syntax error happens when Python can’t understand what you are saying. A run-time error happens when Python understands what you are saying, but runs into trouble when following your instructions.
In English, a syntax error would be like the sentence
Please cat dog monkey.
The grammar of this sentence does not make sense. From the perspective of English grammar, it is missing a verb (action). You cannot understand what you are being asked to do. Syntax means the same thing as grammar.
In English, a run-time error would be like the sentence
Please eat the piano.
The sentence makes sense from a grammatical perspective — there is a verb and noun in the right place — so you know what you are being asked to do. But, you will encounter problems once you start trying to eat the piano (except maybe if you are a termite). This is called a run-time error because it occurs after the program starts running.
We also will talk about logic errors at the end, which means that your program runs without crashing, but still produces the wrong result. An example would be
Please close the back door so that the bugs don’t come in.
This would be a logic error if the front door is also open: even though the command makes sense and you can close the back door, this doesn’t actually accomplish the goal of keeping the bugs out since they’ll still come in the front door.
Common Syntax Errors in Python
Here are a few additional examples of syntax errors that can occur in Python. One very general thing that can occur is that Python will encounter a special symbol in a place that it doesn’t expect.
Python says SyntaxError: invalid syntax
and points with ^
to the exclamation point. The problem is that !
has no meaning in Python. The syntax error would go away if we had put print("Hello, World!")
instead, because then Python would understand that the !
is part of the text with Hello, World
.
Here is another syntax error that is more subtle.
The problem is that class
is a special word in Python. if you had written course
instead of class it would have been fine. Click here to see the list of all special «keywords» in Python.
If you are using quotes around text and you forget the second one, or you are using parentheses and forget the second one, you will get syntax errors:
In this error, EOL is short for End Of Line: Python expected another "
but the line ended before it was found.
Similarly, EOF is short for End Of File: Python kept looking for a )
but the program file ended before it was found.
Sometimes two very similar syntax errors can give two very different error messages. But, every error message is indeed trying to tell you something helpful.
Run-Time Errors
Here are a few common run-time errors. Python is able to understand what the program says, but runs into problems when actually performing the instructions.
- using an undefined variable or function. This can also occur if you use capital letters inconsistently in a variable name:
- dividing by zero, which makes no sense in mathematics. (Why? Since 0 times any number is 0, there is no solution to 1 = X * 0, so 1/0 is undefined.)
- using operators on the wrong type of data
You will find more ways of producing errors as you learn more about Python.
What is the technical difference between syntax and run-time errors? Here is an example comparing a run-time error to a syntax error. Look at the output of each program.
The program with the run-time error created some output, but the one with the syntax error did not. This is because Python runs in two steps:
So, a program with a syntax error will execute no steps at all, but a program with a run-time error will execute the steps that happened before the error occured. |
Logic Errors
Your program might run without crashing (no syntax or run-time errors), but still do the wrong thing. For example, perhaps you want a program to calculate the average of two numbers: the average of x and y is defined as
Why doesn’t this program work?
The average should be
but the program prints 5.0
instead! The error this time has to do with the «order of operations» in arithmetic. When you write x + y / 2
, this has the same mathematical meaning as . To fix the problem, the third line of our program should be written as average = (x + y) / 2
, which makes clear to Python that we really want the value , where we add first and divide afterwards.
You can have logic errors because you designed a program incorrectly, or because you didn’t write code that follows the design correctly (like the average
example). Logic errors can be difficult to spot, especially in a longer program, but as you get better at writing code you will also get better at avoiding logic errors. Lesson 6D will give some tips on avoiding logic errors.
Exercises
Now that the lesson is complete, we have three exercises on debugging (fixing errors in programs). You can try to spot the errors before running the programs, or you can run them first and use Python’s response to determine what needs fixing.
Each program below comes with some code already written for you. You only need to change a few characters (letters/symbols/numbers) to fix each program. The grader will reject any solution that changes too many characters from the original version. This unusual exercise format is meant to be similar to real-world debugging, where people often accidentally include a typo—can you find and fix the typos rather than redoing the whole solution? Select Reset code to default if you need to bring back the original version. |
Once you have squashed all of the bugs, move on to the next lesson!
The second type of error is a runtime error. A program with a runtime error
is one that passed the interpreter’s syntax checks, and started to execute.
However, during the execution of one of the statements in the program, an error
occurred that caused the interpreter to stop executing the program and display
an error message. Runtime errors are also called exceptions because they usually
indicate that something exceptional (and bad) has happened.
Here are some examples of common runtime errors you are sure to encounter:
-
Misspelled or incorrectly capitalized variable and function names
-
Attempts to perform operations (such as math operations) on data of the wrong type (ex.
attempting to subtract two variables that hold string values) -
Dividing by zero
-
Attempts to use a type conversion function such as
int
on a value that can’t be converted to an int
The following program contains various runtime errors. Can you spot any of them?
After locating the error, run the program to see the error message.
Notice the following important differences between syntax errors and runtime errors that can help you as you try to diagnose
and repair the problem:
-
If the error message mentions
SyntaxError
, you know that the problem has to do with syntax: the structure of the code,
the punctuation, etc. -
If the program runs partway and then crashes, you know the problem is a runtime error. Programs with syntax errors
don’t execute even one line.
Stay tuned for more details on the various types of runtime errors. We have a whole section of this
chapter dedicated to that topic.
Check your understanding
- Attempting to divide by 0.
- Python cannot reliably tell if you are trying to divide by 0 until it is executing your program (e.g., you might be asking the user for a value and then dividing by that value—you cannot know what value the user will enter before you run the program).
- Forgetting a colon at the end of a statement where one is required.
- This is a problem with the formal structure of the program. Python knows where colons are required and can detect when one is missing simply by looking at the code without running it.
- Forgetting to divide by 100 when printing a percentage amount.
- This will produce the wrong answer, but Python will not consider it an error at all. The programmer is the one who understands that the answer produced is wrong.
Which of the following is a run-time error?
- The programmer.
- Programmers rarely find all the runtime errors, there is a computer program that will do it for us.
- The interpreter.
- If an instruction is illegal to perform at that point in the execution, the interpreter will stop with a message describing the exception.
- The computer.
- Well, sort of. But it is a special thing in the computer that does it. The stand alone computer without this additional piece can not do it.
- The teacher / instructor.
- Your teacher and instructor may be able to find most of your runtime errors, but only because they have experience looking at code and possibly writing code. With experience runtime errors are easier to find. But we also have an automated way of finding these types of errors.
Who or what typically finds runtime errors?
You have attempted of activities on this page